Ignore configuration Scheduled in Junit tests - configuration

I'm struggling to tests currently because of this configuration :
#Configuration
#RequiredArgsConstructor
#Slf4j
public class ReloadParameters {
private final MetaService metaService;
#Scheduled(initialDelayString = "${reload.parameters.initial-delay}", fixedRateString = "${reload.parameters.delay}")
public void reloadParameters() {
long start = System.currentTimeMillis();
log.info("Reload parameters ...");
metaService.initElements();
long end = System.currentTimeMillis();
log.info("Reload parameters done successfully in [{}]ms...", end - start);
}
}
It's not an auto configuration so i can't just exclude it and as a configuration i can't mock it to ignore it. But the InitElements is really a heavy process and i have to ignore it. how can i do this?

Related

How to load values from custom properties file for junit testing using Mockito?

I have written this test class to check a service. This is in folder test/java/example/demp/Test.java
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource("classpath:conn.properties")
public class DisplayServiceTest {
#Value("${id}")
private String value;
#Mock
private DisplayRepository DisplayReps;
#InjectMocks
private DisplayService DisplayService;
#Test
public void whenFindAll_thenReturnProductList() {
Menu m = new Menu()
m.setId(value); //when I print value its showing 0
List<Display> expectedDisplay = Arrays.asList(m);
doReturn(expectedDisplay).when(DisplayReps).findAll();
List<Display> actualDisplay = DisplayService.findAll();
assertThat(actualDisplay).isEqualTo(expectedDisplay);
}
My properties file
This is in folder test/resources/conn.properties
id=2
What is the right way to set properties from custom properties file? Cause its not loading values ?
Mockito is a mocking framework, so in general you can't load properties file with Mockito.
Now you've used #TestPropertySource which is a part of Spring Testing and it indeed allows loading properties file (that have nothing to do with mockito though). However using it requires running with SpringRunner and in general its good for integration tests, not for unit tests (Spring Runner among primarily loads Spring's application context).
So if you don't want to use spring here, you should do it "manually". There are many different ways to load Properties file from class path (with getClass().getResourceAsStream() to get the input stream pointing on the resource file and the read it into Properties by using Properties#load(InputStream) for example.
You can also use other thirdparties (not mockito), like apache commons io to read the stream with IOUtils class
If you want to integrate with JUnit 4.x you can even create a rule, described here
#TestPropertySource is a spring annotation, so you need to use the SpringRunner.
You can initialize Mockito using MockitoAnnotations.initMocks(this);, check the example below.
#RunWith(SpringRunner.class)
#TestPropertySource("classpath:conn.properties")
public class DisplayServiceTest {
#Value("${id}")
private String value;
// ...
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
// ...
}
You could use just Mockito and JUnit 4. At the #Before method, call MockitoAnnotations.initMocks and load the properties file:
public class DisplayServiceTest {
private String value;
#Mock
private DisplayRepository displayReps;
#InjectMocks
private DisplayService displayService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
Properties prop = loadPropertiesFromFile("conn.properties");
this.value = prop.getProperty("id");
}
private Properties loadPropertiesFromFile(String fileName) {
Properties prop = new Properties();
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream stream = loader.getResourceAsStream(fileName);
prop.load(stream);
stream.close();
} catch (Exception e) {
String msg = String.format("Failed to load file '%s' - %s - %s", fileName, e.getClass().getName(),
e.getMessage());
Assert.fail(msg);
}
return prop;
}
#Test
public void whenFindAll_thenReturnProductList() {
System.out.println("value: " + this.value);
Menu m = new Menu();
m.setId(this.value); // when I print value its showing 0
List<Display> expectedDisplay = Arrays.asList(m);
Mockito.doReturn(expectedDisplay).when(this.displayReps).findAll();
List<Display> actualDisplay = this.displayService.findAll();
Assert.assertEquals(expectedDisplay, actualDisplay);
}
}

Mockito not working in spring boot service class

I am trying to run the test class below with MockBean BaaisnEvcIdMSRepository. It's connecting to the real database (I am getting an exception). Please could you explain why it is not using the mock?
Test class
#RunWith(SpringRunner.class)
#SpringBootTest
public class BaaisnEvcIdMSServiceTest {
#Autowired
BaaisnEvcIdMSService basBaaisnEvcIdMSService;
#MockBean
BaaisnEvcIdMSRepository baaisnEvcIdMSRepository;
#Test
public void getQueryEvcidServiceTest() {
BaaisnEvcIdRequest baaisnEvcIdRequest = new BaaisnEvcIdRequest();
baaisnEvcIdRequest.setLata(650);
baaisnEvcIdRequest.setProduct_type("abc");
baaisnEvcIdRequest.setSvc_type("xyz");
RowMapperServerResponse rowMapperServerResponse = new RowMapperServerResponse();
rowMapperServerResponse.setId(1);
rowMapperServerResponse.setName("sample");
Mockito.when(baaisnEvcIdMSRepository.getQueryEvcidRepository(baaisnEvcIdRequest)).thenReturn(rowMapperServerResponse);
assertEquals(rowMapperServerResponse, basBaaisnEvcIdMSService.getQueryEvcidService(baaisnEvcIdRequest));
}
}
Repository
#Repository
public class BaaisnEvcIdMSRepository {
#Autowired
private JdbcTemplate jdbcTemplate;
#Transactional
public RowMapperServerResponse getQueryEvcidRepository(BaaisnEvcIdRequest baaisnEvcIdRequest) {
RowMapperServerResponse rowMapperServerResponse = jdbcTemplate.queryForObject(
"select * from Master_Circuit WHERE master_ckt_id = ( select max(master_ckt_id) from master_circuit WHERE product = ? AND id_type = ?)",
new Object[]{baaisnEvcIdRequest.getProduct_type(),baaisnEvcIdRequest.getLata()}, new BaaisnRowMapper());
return rowMapperServerResponse;
}
}
exception
java.sql.SQLException: com.informix.asf.IfxASFException: Attempt to connect to database server (tdclsvi1vd002_tcp_ldap) failed.
at com.informix.jdbc.IfxSqliConnect.<init>(IfxSqliConnect.java:1691) ~[jdbc-4.10.8.1.jar:4.10.8.1]
As this is unit test, you do not need spring context. So you will need something like
#RunWith(MockitoJUnitRunner.class)
public class BaaisnEvcIdMSServiceTest {
#InjectMocks
BaaisnEvcIdMSService basBaaisnEvcIdMSService;
#Mock
BaaisnEvcIdMSRepository baaisnEvcIdMSRepository;
#Test
public void getQueryEvcidServiceTest() {
BaaisnEvcIdRequest baaisnEvcIdRequest = new BaaisnEvcIdRequest();
baaisnEvcIdRequest.setLata(650);
baaisnEvcIdRequest.setProduct_type("abc");
baaisnEvcIdRequest.setSvc_type("xyz");
RowMapperServerResponse rowMapperServerResponse = new RowMapperServerResponse();
rowMapperServerResponse.setId(1);
rowMapperServerResponse.setName("sample");
Mockito.when(baaisnEvcIdMSRepository.getQueryEvcidRepository(baaisnEvcIdRequest)).thenReturn(rowMapperServerResponse);
assertEquals(rowMapperServerResponse, basBaaisnEvcIdMSService.getQueryEvcidService(baaisnEvcIdRequest));
}
}
Assuming you indeed aim for the integration test and not for the unit test (in this case, an answer provided by #Yogesh Badke is the way to go), here are some points for consideration:
Place the breakpoint in the test and check the type of the baaisnEvcIdMSRepository. If should be some sort of proxy generated by Mockito.
If you have a proxy indeed, check the reference to the repository in BaaisnEvcIdMSService instance. It also should point on proxy and not on the real class.
Make sure BaaisnEvcIdRequest implements the equals method otherwise Mockito might not find the expectation when the real service runs the code against the repository proxy (assuming that its a proxy indeed as I've described in "2")
Provide the stacktrace of how exactly it fails with exception, it might also contain some beneficial information...

How to use these #DataMongoTest and #SpringBootTest together in integration test

I am trying to write integration test case for one of my rest application which uses mongodb internally to persist the data
#DataMongoTest
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class MainControllerTest {
#LocalServerPort
private int port = 8080;
/* some test cases*/
}
but I am getting below error
java.lang.IllegalStateException: Configuration error: found multiple declarations of #BootstrapWith for test class [com.sample.core.controller.MainControllerTest]: [#org.springframework.test.context.BootstrapWith(value=class org.springframework.boot.test.autoconfigure.data.mongo.DataMongoTestContextBootstrapper), #org.springframework.test.context.BootstrapWith(value=class org.springframework.boot.test.context.SpringBootTestContextBootstrapper)]
looks like these two are mutually exclusive, so how to do the integration testing .
Use #AutoConfigureDataMongo with #SpringBootTest and this will resolve this ambiguity issue. #SpringBootTest and #DataMongoTest cannot be used together.
Answering to a very old post hoping it may help others.
#AutoConfigureDataMongo will connect to real database. In order to still use the embedded mongo, one can initiate the embedded mongoDb manually.
#SpringBootTest(classes = SubscriptionEventApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SubscriptionEventApiIntegrationTest {
#BeforeAll
static void setup() throws Exception {
startEmbeddedMongoDbManually();
}
private static void startEmbeddedMongoDbManually() throws IOException {
final String connectionString = "mongodb://%s:%d";
final String ip = "localhost";
final int port = 27017;
ImmutableMongodConfig mongodConfig = MongodConfig
.builder()
.version(Version.V3_5_5)
.net(new Net(ip, port, Network.localhostIsIPv6()))
.build();
MongodStarter starter = MongodStarter.getDefaultInstance();
mongodExecutable = starter.prepare(mongodConfig);
mongodExecutable.start();
mongoTemplate = new MongoTemplate(MongoClients.create(String.format(connectionString, ip, port)), "test");
}
#AfterAll
static void clean() {
mongodExecutable.stop();
}
#Test
public void test() {
.....
}
}
Purushothaman suggested starting embedded MongoDB server manually. I am suggesting to start it automatically using #DataMongoTest, but creating WebTestClient manually instead.
Kotlin code below, translates to Java trivially:
#DataMongoTest
// #ContextConfiguration may not be needed for your case.
#ContextConfiguration(
classes = [
Application::class,
MainController::class,
// Add more needed classes for your tests here.
// ...
]
)
#TestPropertySource(properties = ["spring.mongodb.embedded.version=4.0.12"])
class MainControllerTest(
#Autowired
private val mainController: MainController,
// Add more beans needed for your tests here.
// ...
) {
// Creating a WebTestClient is easy and
// can be done in different ways.
// Here is one of the possible ways.
private val webTestClient: WebTestClient =
WebTestClient.bindToController(mainController).build()
#Test
fun someTest() {
// ...
}
}

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...

JUnit testing of Ratpack server with Guice injection

I am trying to write a JUnit test with service dependencies being injected.
protected MainClassApplicationUnderTest aut = new MainClassApplicationUnderTest(App.class) {
#Override
protected void addImpositions(final ImpositionsSpec impositions) {
impositions.add(UserRegistryImposition.of(appRegistry -> {
// Allow modifying Injector in tests
return appRegistry.join(Guice.registry(injector));
}));
}
};
private Injector injector = com.google.inject.Guice.createInjector(new Module());
#Before
public void setup () {
injector.injectMembers(this);
}
#After
public void tearDown() {
aut.close();
}
and then using injected services in my test classes:
#Inject
private UserService userService;
This was working fine until I started adding persistence to my app with HikariModule. Now Guice registry creation is a bit more complex:
.join(Guice.registry(b -> b
.module(HikariModule.class, hikariConfig -> {
final String dbUrl = System.getenv("JDBC_DATABASE_URL");
hikariConfig.setJdbcUrl(dbUrl);
})
.module(Module.class)
.bind(DbMigrator.class)
).apply(r))
Because my registry now consists of multiple modules if I have a service that depends on DataSource class coming from HikariModule guice injection fails in tests.
My goal is to allow writing tests in the following fashion:
#Inject // <- not required can be done in #Before method
private UserService userService; // <- Inject it somehow from Application under test
#Test
public void testUser() {
final Result<User, String> userResult = userService.create(new User.Registration());
final ReceivedResponse res = aut.getHttpClient().get("/users/" + user.userId);
assertEquals(200, res.getStatusCode());
}
What is the right approach of injecting service dependencies in tests? I would very much prefer reusing guice modules from MainClassApplicationUnderTest rather than creating my own and overriding them.
After quite some time battling with this issue and help from Ratpack slack I managed to pull this off.
First of all we need to capture our application registry in the local variable.
private Registry appRegistry;
protected MainClassApplicationUnderTest aut = new MainClassApplicationUnderTest(App.class) {
#Override
protected void addImpositions(final ImpositionsSpec impositions) {
impositions.add(UserRegistryImposition.of(r -> {
appRegistry = r;
return Registry.empty();
}));
}
};
It turns out there is a nifty method that starts the application. So when injecting the class we will know that Registry will not be null and we can inject classes.
protected <T> T inject(final Class<T> classOf) {
aut.getAddress();
return appRegistry.get(classOf);
}
Then in test classes we can simply inject any class that is present in the registry.
final UserService userService = inject(UserService.class);
// OR
final DataSource dataSource = inject(DataSource.class);