Test Spring Boot Actuator Customized Endpoint - junit

I'm trying to test a customized actuator endpoint in springboot.
The endpoint is:
#Component
#RestControllerEndpoint(id = "test")
public class TestController {
#PostMapping("/1")
public ResponseEntity<?> testEndpoint(Request request) {
}
}
Then I use MockMvc,
#SpringBootTest
#ActiveProfiles("test")
public class MyControllerTest {
#Autowired
private MyController myController;
#BeforeEach
public void setup() {
this.mock =
MockMvcBuilders.standaloneSetup(myController)
.build();
}
#Test
public void test_1() throws Exception {
Request request = new Request();
request.setStatus("ok");
mock.perform(
post("/1")
.contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(request))
.andExpect(status().isOk())
.andReturn();
}
}
Both post("/test/1") and post("/1") get 404 error. This MockMvc test works for normal controllers.
java.lang.AssertionError: Status expected:<200> but was:<404>
Expected :200
Actual :404
And .yml file is
management:
server.port: 8081
endpoints:
web:
base-path: /api/v1/mycontext
Thanks in advance.

Custom endpoints are added as a sub link under actuator endpoint. So the url of your custom end point should be /actuator/test/1. In this case it should be /api/v1/mycontext/test/1 as you have set management.endpoints.web.base-path.

you probably need to enable the actuator endpoints in your test application config

Related

Spring Integration - no response on reply channel

This is a follow up question to Spring Integration Executor Channel using annotations code sample.
System diagram is attached .
I am trying to test the box highlighted in red by posting a message into 'Common channel' and reading from REPLY_CHANNEL set in the msg.
'Common channel' is a publish subscribe channel.
REPLY_CHANNEL is a QueueChannel.
Since this is a JUnit test, I have mocked jdbcTemplate, datasource and the Impl to ignore any DB calls.
My issue is:
When I post a message onto 'Common Channel', I do not receive any message on the REPLY_CHANNEL. The junit keeps waiting for a response.
What should I change to get a response on the REPLY_CHANNEL?
#RunWith(SpringRunner.class)
#SpringBootTest
#ContextConfiguration(loader = AnnotationConfigContextLoader.class) --------- 1
#ActiveProfiles("test")
public class QueuetoQueueTest {
#Configuration
static class ContextConfiguration { ------------------------------------- 2
#Bean(name = "jdbcTemplate")
public JdbcTemplate jdbcTemplate() {
JdbcTemplate jdbcTemplateMock = Mockito.mock(JdbcTemplate.class);
return jdbcTemplateMock;
}
#Bean(name = "dataSource")
public DataSource dataSource() {
DataSource dataSourceMock = Mockito.mock(DataSource.class);
return dataSourceMock;
}
#Bean(name = "entityManager")
public EntityManager entityManager() {
EntityManager entityManagerMock = Mockito.mock(EntityManager.class);
return entityManagerMock;
}
#Bean(name = "ResponseChannel")
public QueueChannel getReplyQueueChannel() {
return new QueueChannel();
}
//This channel serves as the 'common channel' in the diagram
#Bean(name = "processRequestSubscribableChannel")
public MessageChannel getPublishSubscribeChannel() {
return new PublishSubscribeChannel();
}
}
#Mock
DBStoreDaoImpl dbStoreDaoImpl;
#Test
public void testDBConnectivity() {
Assert.assertTrue(true);
}
#InjectMocks -------------------------------------------------------------- 3
StoretoDBConfig storetoDBConfig = new StoretoDBConfig();
#Autowired
#Qualifier("ResponseChannel")
QueueChannel ResponseChannel;
#Autowired
#Qualifier("processRequestSubscribableChannel")
MessageChannel processRequestSubscribableChannel;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void outboundtoQueueTest() {
try {
when(dbStoreDaoImpl.storeToDB(any()))
.thenReturn(1); ----------------------------------------------- 4
//create message
Message message = (Message<String>) MessageBuilder
.withPayload("Hello")
.setHeader(MessageHeaders.REPLY_CHANNEL, ResponseChannel)
.build();
//send message
processRequestSubscribableChannel.send(message);
System.out
.println("Listening on InstructionResponseHandlertoEventProcessorQueue");
//wait for response on reply channel
Message<?> response = ResponseChannel.receive(); ----------------------- 5
System.out.println("***************RECEIVED: "
+ response.toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
Load 'ContextConfiguration' for JUnit so that DB is not accessed.
This is how you load custom configuration in JUnit as per https://spring.io/blog/2011/06/21/spring-3-1-m2-testing-with-configuration-classes-and-profiles
Inside the config class, we mock jdbcTemplate, dataSource, entityManager and define the 'common channel' on which the request is posted and ResponseChannel.
Inject jdbcTemplate, dataSource mock into StoretoDBConfig so that the DB is not hit
Mock DaoImpl class so that DB calls are ignored
The test blocks here because there is no response on the REPLY_CHANNEL
UPDATED CODE:
Code inside 5 (the class that reads from common channel):
#Configuration
class HandleRequestConfig {
//Common channel - refer diagram
#Autowired
PublishSubscribeChannel processRequestSubscribableChannel;
//Step 9 - This channel is used to send queue to the downstream system
#Autowired
PublishSubscribeChannel forwardToExternalSystemQueue;
public void handle() {
IntegrationFlows.from("processRequestSubscribableChannel") // Read from 'Common channel'
.wireTap(flow->flow.handle(msg -> System.out.println("Msg received on processRequestSubscribableChannel"+ msg.getPayload())))
.handle(RequestProcessor,"validateMessage") // Perform custom business logic - no logic for now, return the msg as is
.wireTap(flow->flow.handle(msg -> System.out.println("Msg received on RequestProcessor"+ msg.getPayload())))
.channel("forwardToExternalSystemQueue"); // Post to 'Channel to another system'
}
}
//Code inside step 8 - 'Custom Business Logic'
#Configuration
class RequestProcessor {
public Message<?> validateMessage(Message<?> msg) {
return msg;
}
}
WHAT I AM TRYING TO ACHIEVE:
I have individual junit test cases for the business logic. I am trying to test that when the request is posted into the 'common channel', the response is received on 'channel to another system'.
Why I cannot use the original ApplicationContext: Because it connects to the DB, and I do not want my JUnit to connect to the DB or use an embedded database. I want any calls to the DB to be ignored.
I have set the reply channel to 'ResponseChannel', shouldn't the 'Custom Business Logic' send its response to 'ResponseChannel'?
If I have to listen on a different channel for the response, I am willing to do so. All I want to test is whether the message I am sending on 'common channel' is received on 'channel to other system'.
UPDATE 2:
Addressing Artem's questions.
Thankyou Artem for your suggestions.
Is 'HandlerRequestConfig' included in the test configuration? - We cannot directly call the handle() method. Instead I thought if I post on 'processRequestSubscribableChannel', the handle() method inside HandleRequestConfig will be invoked since it listens on the same channel. Is this wrong? How do I test HandleRequestConfig.handle() method then?
I added wiretap to the end of each step in HandleRequestConfig (code updated). I find that none of the wiretap message is printed. This means that the msg I am posting is not even reaching the input channel 'processRequestSubscribableChannel'. What am I doing wrong?
NOTE: I tried removing the 'processRequestSubscribableChannel' bean inside Configuration (so that the actual 'processRequestSubscribableChannel' in the applicationContext is used). I am getting an unsatisfied dependency error - Expected atleast 1 bean with configuration PublishSubscribeChannel.
Update 3: Posted details Artem requested.
#RunWith(SpringRunner.class)
#SpringBootTest
public class QueuetoQueueTest {
// Step 1 - Mocking jdbcTemplate, dataSource, entityManager so that it doesn't connect to the DB
#MockBean
#Qualifier("jdbcTemplate")
JdbcTemplate jdbcTemplate;
#MockBean
#Qualifier("dataSource")
public DataSource dataSource;
#MockBean
#Qualifier("entityManager")
public EntityManager entityManager;
#Bean(name = "ResponseChannel")
public PublishSubscribeChannel getReplyQueueChannel() {
return new PublishSubscribeChannel();
}
//Mocking the DB class
#MockBean
#Qualifier("dbStoreDaoImpl")
DBStoreDaoImpl dbStoreDaoImpl ;
//Inject the mock objects created above into the flow that stores data into the DB.
#InjectMocks
StoretoDBConfig storetoDBConfig = new StoretoDBConfig();
//Step 2 - Injecting MessageChannel used in the actual ApplicationContext
#Autowired
#Qualifier("processRequestSubscribableChannel")
MessageChannel processRequestSubscribableChannel;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void outboundtoQueueTest() {
try {
when(dbStoreDaoImpl.storeToDB(any()))
.thenReturn(1);
//create message
Message message = (Message<?>) MessageBuilder
.withPayload("Hello")
.build();
//send message - this channel is the actual channel used in ApplicationContext
processRequestSubscribableChannel.send(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
ERROR I AM GETTING: The code tries to connect to the DB and throws an error.
UPDATE 1: Code inside StoretoDBConfig
#Configuration
#EnableIntegration
public class StoretoDBConfig {
#Autowired
DataSource dataSource;
/*
* Below code is irrelevant to our current problem - Including for reference.
*
* storing into DB is delegated to a separate thread.
*
* #Bean
* public TaskExecutor taskExecutor() {
* return new SimpleAsyncTaskExecutor();
* }
*
* #Bean(name="executorChannelToDB")
* public ExecutorChannel outboundRequests() {
* return new ExecutorChannel(taskExecutor());
* }
* #Bean(name = "DBFailureChannel")
* public static MessageChannel getFailureChannel() {
* return new DirectChannel();
* }
* private static final Logger logger = Logger
* .getLogger(InstructionResponseHandlerOutboundtoDBConfig.class);
*/
#Bean
public IntegrationFlow handle() {
/*
* Read from 'common channel' - processRequestSubscribableChannel and send to separate thread that stores into DB.
*
/
return IntegrationFlows
.from("processRequestSubscribableChannel")
.channel("executorChannelToDB").get();
}
}
CODE THAT STORES INTO DB ON THE SEPARATE THREAD:
#Repository
public class DBStoreDaoImpl implements DBStoreDao {
private JdbcTemplate jdbcTemplate;
#Autowired
public void setJdbcTemplate(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
#Override
#Transactional(rollbackFor = Exception.class)
#ServiceActivator(inputChannel = "executorChannelToDB")
public void storetoDB(Message<?> msg) throws Exception {
String insertQuery ="Insert into DBTable(MESSAGE) VALUES(?)";
jdbcTemplate.update(insertQuery, msg.toString());
}
}
Please, show us what is subscribed to that Common channel. Your diagram somehow is not related to what you show us. The code you demonstrate is not full.
The real problem with the replyChannel that something really has to send a message to it. If your flow is just one-way - send, store and nothing to return, - then you indeed won't get anything for this one. That's why would to show those channel adapters.
The best way to observe the message journey is to turn on debug logging for the org.springframework.integration category.
Although I see that you declare those channels as is in the ContextConfiguration and there is really no any subscribers to the getRequestChannel. Therefore nobody is going to consume your message and, of course, nobody is going to send you a reply.
Please, reconsider your test class to use the real application context. Otherwise it is fully unclear what you would like to achieve if you really don't test your flow...

How to correctly load Firebase ServiceAccount json resource with Spring MVC?

I'm trying to connect my Spring MVC (not Spring Boot) application to Firebase. My application's folder structure looks like this:
folder structure
The problem is that I don't know where to place the api key json file, how to load the resource, and the correct order of the method calls.
I tried loading the resource the way shown below. Before that I also tried using ClassLoader to load it from the WEB-INF folder and it worked, but changed the code and kept receiving NullPointer Exception (why not FileNotFound Exception?) for the InputStream and couldn't restore the previous state.
With the current state I keep receiving FileNotFound Exception as I'm am not able to load the resource no matter how much I googled "Spring MVC load resource" and as I checked the debugger the service account's "init" method with #PostConstruct isn't running at starting the server.
I understand that I should be able to load the resource and call the "init" method in order to make it work. (I suppose it's enough to call it once after creating the bean and before using firebase methods) But I just couldn't come up with a working implementation.
I used examples from here:
https://github.com/savicprvoslav/Spring-Boot-starter
(Bottom of the Page)
My Controller Class:
#Controller
#RequestMapping("/firebase")
public class FirebaseController {
#Autowired
private FirebaseService firebaseService;
#GetMapping(value="/upload/maincategories")
public void uploadMainRecordCategories() {
firebaseService.uploadMainRecordCategories();
}
My Service Class:
#Service
public class FirebaseServiceBean implements FirebaseService {
#Value("/api.json")
Resource apiKey;
#Override
public void uploadMainRecordCategories() {
// do something
}
#PostConstruct
public void init() {
try (InputStream serviceAccount = apiKey.getInputStream()) {
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.setDatabaseUrl(FirebaseStringValue.DB_URL).build();
FirebaseApp.initializeApp(options);
} catch (IOException e) {
e.printStackTrace();
}
}
}
how about saving value in a spring property and using #Value("${firebase.apiKey}")?
Alternatively, save path to file in property and reference that in #Value()
#Value("${service.account.path}")
private String serviceAccountPath;
In application.properties:
service.account.path = /path/to/service-account.json
then config code:
private String getAccessToken() throws IOException {
GoogleCredential googleCredential = GoogleCredential
.fromStream(getServiceAccountInputStream())
.createScoped(Collections.singletonList("https://www.googleapis.com/auth/firebase.messaging"));
googleCredential.refreshToken();
return googleCredential.getAccessToken();
}
private InputStream getServiceAccountInputStream() {
File file = new File(serviceAccountPath);
try {
return new FileInputStream(file);
} catch (FileNotFoundException e) {
throw new RuntimeException("Couldn't find service-account.json");
}
}

spring boot: change print console to json rest

Actually, my project in Spring send values from the database via console, like this:
Console image, but I want to send this values via JSON like a rest API, but I don't know how to change that.
{
"depositarios": {
"correo": "correo",
"nombre": "nombre",
"numTel": "numTel",
"pApellido": "pApellido",
"SApellido": "sAellido"
}
}
this is my main class:
#SpringBootApplication
#ComponentScan("com.abner.springpostgresql.service.impl, com.abner.springpostgresql.dao.imp")
public class SpringPostgresqlApplication {
public static void main(String[] args) {
ApplicationContext context= SpringApplication.run(SpringPostgresqlApplication.class, args);
depoService depoService =context.getBean(depoService.class);
depoService.loadAllDepo();
}
}
this is my entired project source https://github.com/abnercoronado/restpostgresql
You have to create a RestController using the #RestController annotation like this:
#RestController
public class MyRestController {
#RequestMapping(value = "/personas", method = RequestMethod.GET)
public List<Persona> listaPersonas() {
// This is just a sample. Here you could bring your data form your db
List<Persona> lista = new ArrayList<Persona>();
Persona p = new Persona();
p.setNombre("angel");
p.setEdad(20);
lista.add(p);
return lista;
}
}
The value of the #RequestMapping annotation ("/personas" in this example) will be the endpoint. So when you access to the endpoint http://localhost:8080/personas (asuming that your app is running on http://localhost:8080) then you will get your data as json.
Here is an example of how to do it.
Here is another example (en espaƱol) that could help you.
You can use ObjectMapper to convert your pojo or object to JSON String and send where ever you wanted using thier API or anything.
Or you can create Rest Method and Access the API would return you the Json Value.
#RestController
public class MyRestController {
#RequestMapping(value = "/depo", method = RequestMethod.GET)
public List<?> getDepo() {
ApplicationContext context= SpringApplication.run(SpringPostgresqlApplication.class, args);
depoService depoService =context.getBean(depoService.class);
List<?> lista = depoService.loadAllDepo();
return lista;
}
Another way of doing.
#RestController
public class MyRestController {
#RequestMapping(value = "/depo", method = RequestMethod.GET)
public List<Depo> getDepo() {
ApplicationContext context= SpringApplication.run(SpringPostgresqlApplication.class, args);
depoService depoService =context.getBean(depoService.class);
List<Depo> lista = depoService.loadAllDepo();
return lista;
}
Once you start your server, you can run this by doing localhost:8080/depo. you can also return XML.

How to send POST request by Spring cloud Feign

It's my Feign interface
#FeignClient(
name="mpi",
url="${mpi.url}",
configuration = FeignSimpleEncoderConfig.class
)
public interface MpiClient {
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<String> getPAReq(#QueryMap Map<String, String> queryMap
);
}
and my custom configuration
public class FeignSimpleEncoderConfig {
public static final int FIVE_SECONDS = 5000;
#Bean
public Logger.Level feignLogger() {
return Logger.Level.FULL;
}
#Bean
public Request.Options options() {
return new Request.Options(FIVE_SECONDS, FIVE_SECONDS);
}
#Bean
#Scope("prototype")
public Feign.Builder feignBuilder() {
return Feign.builder()
.encoder(new FormEncoder());
}
}
If I send request like this I see that my request send Content-Type: application/json;charset=UTF-8.
But if I set content type
consumes = "application/x-www-form-urlencoded"
I've this error message
feign.codec.EncodeException: Could not write request: no suitable HttpMessageConverter found for request type [java.util.HashMap] and content type [application/x-www-form-urlencoded]
at org.springframework.cloud.netflix.feign.support.SpringEncoder.encode(SpringEncoder.java:108) ~[spring-cloud-netflix-core-1.1.7.RELEASE.jar:1.1.7.RELEASE]
How to send POST request, I think I should make something more with Encoder.
Thanks for your help.
First of all you should change your Feign interface like this:
#FeignClient (
configuration = FeignSimpleEncoderConfig.class
)
public interface MpiClient {
#RequestMapping(method = RequestMethod.POST)
ResponseEntity<String> getPAReq(Map<String, ?> queryMap);
}
Then you should set the encoder during feign configuration:
public class FeignSimpleEncoderConfig {
#Bean
public Encoder encoder() {
return new FormEncoder();
}
}
It seems to me that Map is not valid for form body. MultiValueMap works just fine.
Feign client:
#FeignClient(name = "name", url="url", configuration = FromUrlEncodedClientConfiguration.class)
public interface PayPalFeignClient {
#RequestMapping(value = "/", method = POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
#Headers("Content-Type: application/x-www-form-urlencoded")
String foo(MultiValueMap<String, ?> formParams);
}
Config:
#Configuration
public class FromUrlEncodedClientConfiguration {
#Autowired
private ObjectFactory<HttpMessageConverters> messageConverters;
#Bean
#Primary
#Scope(SCOPE_PROTOTYPE)
Encoder feignFormEncoder() {
return new FormEncoder(new SpringEncoder(this.messageConverters));
}
}
Gradle dependencies:
compile group: 'io.github.openfeign.form', name: 'feign-form', version: '2.0.2'
compile group: 'io.github.openfeign.form', name: 'feign-form-spring', version: '2.0.5'
After that all you have to do is call it with a MultivalueMap parameter.
Specify the correct encoder for handle form encoded request
you can specify multi encoder example json/xml/formhttpurl encoded
#Bean
public Encoder feignEncoder() {
ObjectFactory<HttpMessageConverters> objectFactory = () ->
new HttpMessageConverters(new FormHttpMessageConverter());
return new SpringEncoder(objectFactory);
}
Important FormHttpMessageConverter serialize only MultiValueMap subsclass
im my case it was due to outdated version of lombok, updating to 1.18.16 resolve it

Junit code coverage in not covering the code in java using eclemma

I have written the junit test case of a API helper class but when I am checking the code coverage, the helper class is showing as 0 percent. I am not sure what is the issue.
Here is the sample code:
public Class ClassHelper{
public Response invoke() {
ClassVO classVO = new ClassVO(pString);
ClassOutputVO outputVO = getSoapServiceBridgeProvider().getSoapServiceBridge(classVO).invokeProces()
...
return response
}
}
the testclass is given below:
#Test
public void testInvoke() throws Exception{
PowerMockito.whenNew(ClassVO.class).withArguments(Mockito.anyString()).thenReturn(mockVO);
Response actualResponse = (Response) classHelper.invoke();
can someone please help me with the issue? Its working with other helper classes but only happening with api helpers.
the working example has below:
#Component
public class TestHelper {
public Reponse updateRole(Request request) throws Exception {
ClassVO vo = new ClassVO();
vo.setRoleId(request.getRoleId());
vo.setRoleName(request.getRoleName());
Response response = new Response;
response.setRoleId(vo.getRoleId());
response.setRoleName(vo.getRoleName));
}
}
Test class:
#ContextConfiguration
#RunWith(PowerMockRunner.class)
#PowerMockIgnore("javax.management.*")
#PrepareForTest({ DriverManager.class })
public class TestHelperTest {
#InjectMocks
private TestHelper testHelper;
#Test
public void testUpdateRole() throws ADTMBaseException{
Request request = new Request();
request.setRoleId(8);
request.setRoleName(JUnitTestConstants.ROLE_NAME);
Reponse actualResult = testHelper.updateRole(request);
}