JUnit testing of Ratpack server with Guice injection - junit

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);

Related

How do I make a JMS ObjectMessage for a Unit Test?

I'm trying to write a unit test for an MDB. The goal of my test is to make sure that the logic in the MDB can identify the correct type of object in the ObjectMessage and process it. However, I can't figure out how to make an ObjectMessage so I can test it. I keep getting null pointer exceptions.
Here is my unit test:
/**
* Test of the logic in the MDB
*/
#RunWith(JMockit.class)
#ExtendWith(TimingExtension.class)
class MDBTest
{
protected MyMDB mdb;
#BeforeEach
public void setup() throws NamingException, CreateHeaderException, DatatypeConfigurationException, PropertiesDataException
{
mdb = new MyMDB();
}
/**
* Test the processing of the messages by the MDB
*/
#Test
void testReceivingMessage() throws JMSException, IOException
{
MyFirstObject testMsg = getTestMessage();
ObjectMessage msg = null;
Session session = null;
new MockUp<ObjectMessage>()
{
#Mock
public void $init()
{
}
#Mock
public Serializable getObject()
{
return testMsg;
}
};
new MockUp<Session>()
{
#Mock
public void $init()
{
}
#Mock
public ObjectMessage createObjectMessage(Serializable object)
{
return msg;
}
};
// !!!! Null pointer here on Session !!!!
ObjectMessage msgToSend = session.createObjectMessage(testMsg);
mdb.onMessage(msgToSend);
assertEquals(1, mdb.getNumMyFirstObjectMsgs());
}
/**
* Create a Test Message
*
* #return the test message
* #throws IOException
*/
protected MyFirstObject getTestMessage) throws IOException
{
MyFirstObject myObj = new MyFirstObject();
myObj.id = 0123;
myObj.description = "TestMessage";
return myObj;
}
}
I feel like I should be able to initialize Session somehow, but I need to do it without using an additional library like Mockrunner.
Any suggestions?
I would try to address this in a different style. Provide a mock client, that will just mock the right API.
We should mock only a set of functions required for message retrieval and processing but that means we might have to provide a custom implementation for some of the APIs available in the EJB/JMS library. The mock client will have a function to push messages on a given topic/queue/channel, message can be simple String.
A simple implementation might look like this, in this other methods have been omitted for simplicity.
// JMSClientImpl is an implementation of Connection interface.
public class MyJmsTestClient extends JMSClientImpl{
Map<String, String> channelToMessage = new ConcurrentHashMap<>();
public Map<String, String> getMessageMap(){
return channelToMessage;
}
public void enqueMessage(String channel, String message){
channelToMessage.put(channe, message);
}
#Override
public Session createSession(){
return new MyTestSession(this);
}
}
// A class that implements some of the methods from session interface
public MyTestSession extends SessionImpl{
private MyJmsTestClient jmsClient;
MyTestSession(MyJmsTestClient jmsClient){
this.jmsClient = jmsClient;
}
// override methods that fetches messages from remote JMS
// Here you can just return messages from MyJmsTestClient
// override other necessary methods like ack/nack etc
MessageConsumer createConsumer(Destination destination) throws JMSException{
// returns a test consume
}
}
A class that implements methods from MessageConsumer interface
class TestMessageConsumer extends MessageConsumerImpl {
private MyJmsTestClient jmsClient;
private Destination destination;
TestMessageConsumer(MyJmsTestClient jmsClient, Destination destination){
this.jmsClient = jmsClient;
this.destination = destination;
}
Message receive() throws JMSException{
//return message from client
}
}
There's no straight forward, you can see if there're any library that can provide you embedded JMS client feature.

How to Unit Test Resilience4j circuit breaker fallback methods

I am trying to Unit test the Resilience4j CircuitBreaker configuration for my service. But I am unable to call the fallback method when I throw HttpServerErrorException. However I try to mock the objects the call is not going to the fallback method.
The fallback works fine. When AService fails, the call is directed to fallback method calling BService. How do I write test cases to verify them?
I am new to Resilience4j and fallback patterns. Please help me to test this.
Service class
#AllArgsConstructor
#Service
public class ServiceImpl implements Service {
#NonNull
private AService aService;
#NonNull
private BService bService;
#CircuitBreaker(name = "resilienceConfig", fallbackMethod = "fallback")
#TimeLimiter(name = "resilienceConfig")
#Override
public Mono<List<PojoClass>> method(Mono<PojoClass2> pc) {
return pc
.map(PojoClass2::getPojoClass2)
.flatMapMany(Flux::fromIterable)
.map(Pojo3::getId)
.flatMap(service -> aService.method(param1, param2))
.flatMapMany(Flux::fromIterable)
.map(this::changeValues)
.collectList();
}
public Mono<List<PojoClass>> fallback(Mono<PojoClass2> pc, Throwable ex) {
return pc
.map(PojoClass2::getPojoClass2)
.flatMapMany(Flux::fromIterable)
.map(Pojo3::getId)
.flatMap(service -> bService.method(param1, param2))
.flatMapMany(Flux::fromIterable)
.map(this::changeValues)
.collectList();
}
}
ServiceTest
#SpringBootTest(classes = Application.class)
#RunWith(MockitoJUnitRunner.class)
public class ServiceImplTest {
#Mock
private AService aService;
#Mock
private BService bService;
#InjectMocks
ServiceImpl serviceImpl;
#Test
public void enrichException() {
doThrow(new HttpServerErrorException(HttpStatus.INTERNAL_SERVER_ERROR))
.when(aService)
.method(param1, param2);
Mono<List<PojoClass>> pojo = serviceImpl.method(param1);
}
}

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() {
// ...
}
}