Spring Boot Test: Inject different Resources for each unit tests - java

I created a #SpringBootTest-annotated class to test the HTTP responses of a REST endpoint.
I compare each HTTP JSON response with the content of a Resource.
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SampleApplicationTests {
#Autowired
private MockMvc mockMvc;
#Value("classpath:data/result1.json")
private Resource result1;
#Value("classpath:data/result2.json")
private Resource result2;
#Test
public void test1() {
mockMvc.perform(MockMvcRequestBuilders.
get("/api/persons")).
andExpect(MockMvcResutMatchers.status().isOk()).
andExpect(MockMvcResutMatchers.content().json(asString(result1)));
}
#Test
public void test2() {
mockMvc.perform(MockMvcRequestBuilders.
get("/api/persons/1")).
andExpect(MockMvcResutMatchers.status().isOk()).
andExpect(MockMvcResutMatchers.content().json(asString(result2)));
}
...
private static String asString(Resource resource) {
try {
return IOUtils.toString(resource.getInputStream());
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
}
I would like to know if there is a more elegant way to inject resources at the level of each test.
I know I can use ResourceLoader to load resources for each test:
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SampleApplicationTests {
#Autowired
private ResourceLoader resourceLoader = null;
#Test
public void test1() {
String result = asString(resourceLoader.getResource("classpath:data/result1.json");
mockMvc.perform(MockMvcRequestBuilders.
get("/api/persons")).
andExpect(MockMvcResutMatchers.status().isOk()).
andExpect(MockMvcResutMatchers.content().json(result));
}
...
}
But I would like to know if there is an Annotation that allows to load a resource for each test.
Something like the following:
#AutoConfigureMockMvc
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class SampleApplicationTests {
#Test
#TestResource("classpath:data/result1.json")
public void test1(Resource result) {
mockMvc.perform(MockMvcRequestBuilders.
get("/api/persons")).
andExpect(MockMvcResutMatchers.status().isOk()).
andExpect(MockMvcResutMatchers.content().json(result));
}
...
}
Does anyone have any suggestions?

SpringBootTest is annotated with #SpringExtension which resolves arguments for method parameters as well:
#Test
public void test1(#Value("classpath:data/result1.json") Resource result1) {
// ...
}
#Test
public void test2(#Value("classpath:data/result2.json") Resource result2) {
// ...
}

Related

Test a controller with Junit?

I had some trouble setting up unit test with my spring boot application. My main issue is with the "model" object that's needed in my controller, but I can't find a way to recreate it in my test, which is required to use my function.
here are the function I want to test
#Controller
public class AjoutAbscenceControler {
#Autowired
private AbsenceRepository absenceRepository;
#RequestMapping(value = { "/addAbsence" }, method = RequestMethod.GET)
public String showAddAbsencePage(Model model) {
Absence absence = new Absence();
model.addAttribute("Absence", absence);
return "addAbsence";
}
#RequestMapping(value = { "/addingAbsence" }, method = RequestMethod.POST)
public String saveAbsence(Model model, #ModelAttribute("absence") Absence absence) {
if (absence.getName() != null && absence.getName().length() > 0) {
absenceRepository.save(absence);
}
return "redirect:/userList";
}
}
I did try something like that
#RunWith(MockitoJUnitRunner.class)
public class AjoutAbscenceControlerTest {
#Mock
VacationRepository vacationRepository;
#Mock
CategoryRepository categoryRepository;
#InjectMocks
AjoutAbscenceControler controler;
public MockMvc mockMvc;
#Before
public void setUp() throws Exception{
mockMvc = MockMvcBuilders.standaloneSetup(controler).build();
}
#Test
public void showAddAbsencePagetest() {
AjoutAbscenceControler ajoutAbscenceControler =new AjoutAbscenceControler();
assertEquals("addAbsence",ajoutAbscenceControler.showAddAbsencePage(controler));
}
}
but I don't find any way to create a springfarmwork.ui.Model
If you're testing the logic of your controller you probably shouldn't create a Model object, but mock it, and verify the interactions against it:
#Mock
private Model model;
#Test
public void showAddAbsencePagetest() {
// Should probably be initialized in a #Before method,
// Initialized here for clarity only
AjoutAbscenceControler ajoutAbscenceControler = new AjoutAbscenceControler();
assertEquals("addAbsence", ajoutAbscenceControler.showAddAbsencePage(model));
Mockito.verify(model).addAttribute(eq("Absence"), any(Absence.class));
}

Apache camel Junit mock issue

I am writing a JUnit test case for a Route class. I'm facing a problem while mocking ServiceClass inside the Processor class.
public class SaveRouteTest extends CamelTestSupport {
private Exchange exchange;
protected ProducerTemplate template;
private SaveRequestBuilder saveRequestBuilder;
private SaveRoute route;
private SaveProcessor saveProcessor;
private ApplicationContext springContext = createApplicationContext();
#Mock
SaveServiceClient saveServiceClient;//Not able to mock this class
#BeforeClass
public void prepareTestCamelContext() throws Exception {
route = springContext.getBean("saveRoute", saveRoute.class);
saveProcessor = springContext.getBean("saveProcessor",
SaveProcessor.class);
saveRequestBuilder = springContext.getBean("saveRequestBuilder",
SaveRequestBuilder.class);
}
#BeforeMethod
public void init() throws SQLException, ServiceException {
MockitoAnnotations.initMocks(this);
exchange = new DefaultExchange(context);
}
#Override
protected RouteBuilder[] createRouteBuilders() throws Exception {
template = context.createProducerTemplate();
return new RouteBuilder[]{route};
}
#Test
public void testHotelCommitTransactionRouteSuccessReturn() throws
Exception {
when(saveServiceClient.invokeServiceWithName(anyObject()).
thenReturn("Response");
exchange.getIn().setBody("Request detail");
exchange = template.send("direct:SaveRoute",exchange);
}
protected ApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext("classpath*:config/spring/testContext.xml");
}
}
#Component
public class SaveRoute extends SPRouteBuilder {
#Autowired
private SaveProcessor saveProcessor;
#Override
public void configure() throws Exception {
from("direct:SaveRoute")
.routeId("save")
.to("direct:ProcessSaveFlow")
.end();
from("direct:ProcessSaveFlow")
.process(saveProcessor)
.end();
}
}
public class SaveProcessor implements Processor {
#Autowired
SaveServiceClient saveServiceClient;
#Override
public void process(Exchange exchange) throws Exception {
//This line of code not able to mock
String response = saveServiceClient.invokeServiceWithName(exchange);
exchange.getIn().setBody(response);
}
}
How to resolve mocking of saveServiceClient.invokeServiceWithName? The debugger is always going inside this method. I tried using both mock objects and an injected mock. I can't make the method call directly.
You are creating a mock object, however you are not injecting it anywhere (normally you are doing it with #InjectMocks annotation - read about it).
I think there are several possibilities:
Provide a #MockBean object, which will be considered as a bean candidate in context.
There is a code example for mocking beans.
#RunWith ( CamelSpringRunner.class )
#SpringBootTest
public class RouteBuilderTest extends CamelSpringTestSupport {
#Autowired
private ApplicationContext applicationContext;
#MockBean
private ServiceClient serviceClient;
#Override
public void setUp() throws Exception {
MockitoAnnotations.initMocks( this );
super.setUp();
}
#Override
public void tearDown() {
}
#Test
public void test() {
when( serviceClient.doStuff() ).thenReturn( "mockedResponse" );
}
}
Mock SaveProcessor and inject it to Route class - you shouldn't take care of ServiceClient, because you are trying to test too much. Tests for SaveProcessor should be separated, tests for route don't need this logic.

DirtiesContext not clearing ApplicationContext in Spring unit tests

I have a class called SomeBean and two tests that are configured with Stubs for different scenarios. I am using Spring Boot.
The second test is supposed to pass without Exception because there is no stubbing that I did to throw Exception.
The DirtiesContext is not working as well. If I remove the commented code in Test2.java I get the test to pass. I would like to remove the unnecessary subbing by using something similar to DirtiesContext.
I may be missing something basic. Can someone point to what I am doing incorrect.
#Service
public class SomeBeanProcessor {
#Autowired
private BeanValidator beanValidator;
public ResultBean process(SomeBean sb) throws ValidationException{
beanValidator.validateBean(sb);
//Do some processing and return ResultBean;
}
}
Test1.java
RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApp.class})
#WebAppConfiguration
#ContextConfiguration(classes=Test1.Test1Config.class) {
public class Test1 {
#Configuration
static class Test1Config {
#Bean
public BeanValidator getSomeRequestValidator() {
return new BeanValidator() {
public void validateBean(SomeBean bean) throws ValidationException {
throw new ValidationException("Validation failed");
}
};
}
}
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
private SomeBeanProcessor aBeanProcessor;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Test
#DirtiesContext
public void doTestValidationErrors() throws ValidationException{
SomeBean sb = new SomeBean();
this.aBeanProcessor.process(sb);
Assert.fail("Should throw exception");
}
}
Test2.java
RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApp.class})
#WebAppConfiguration
#ContextConfiguration(classes=Test2.Test2Config.class) {
public class Test2 {
#Configuration
static class Test2Config {
//#Bean
//public BeanValidator getSomeRequestValidator() {
// return new BeanValidator() {
// public void validateBean(SomeBean bean) throws ValidationException { //Do nothing
// }
// };
//}
}
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Autowired
private SomeBeanProcessor aBeanProcessor;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Test
#DirtiesContext
public void doTestSuccess() throws ValidationException{
SomeBean sb = new SomeBean();
this.aBeanProcessor.process(sb);
}
}

Testing a controller with an auto wired component is null when calling the controller from a test case

I have a controller
#RestController
public class Create {
#Autowired
private ComponentThatDoesSomething something;
#RequestMapping("/greeting")
public String call() {
something.updateCounter();
return "Hello World " + something.getCounter();
}
}
I have a component for that controller
#Component
public class ComponentThatDoesSomething {
private int counter = 0;
public void updateCounter () {
counter++;
}
public int getCounter() {
return counter;
}
}
I also have a test for my controller.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ForumsApplicationTests {
#Test
public void contextLoads() {
Create subject = new Create();
subject.call();
subject.call();
assertEquals(subject.call(), "Hello World 2");
}
}
The test fails when the controller calls something.updateCounter(). I get a NullPointerException. While I understand it's possible to add #Autowired to a constructor I would like to know if there is anyway to do this with an #Autowired field. How do I make sure the #Autowired field annotation works in my test?
Spring doesn't auto wire your component cause you instantiate your Controller with new not with Spring, so Component is not instatntiated
The SpringMockMvc test check it correct:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
public class CreateTest {
#Autowired
private WebApplicationContext context;
private MockMvc mvc;
#Before
public void setup() {
mvc = MockMvcBuilders
.webAppContextSetup(context)
.build();
}
#Test
public void testCall() throws Exception {
//increment first time
this.mvc.perform(get("/greeting"))
.andExpect(status().isOk());
//increment secont time and get response to check
String contentAsString = this.mvc.perform(get("/greeting"))
.andExpect(status().isOk()).andReturn()
.getResponse().getContentAsString();
assertEquals("Hello World 2", contentAsString);
}
}
The #Autowired class can be easily mocked and tested with MockitoJUnitRunner with the correct annotations.
With this you can do whatever you need to do with the mock object for the unit test.
Here is a quick example that will test the Create method call with mocked data from ComponentThatDoesSomething.
#RunWith(MockitoJUnitRunner.class)
public class CreateTest {
#InjectMocks
Create create;
#Mock
ComponentThatDoesSomething componentThatDoesSomething;
#Test
public void testCallWithCounterOf4() {
when(componentThatDoesSomething.getCounter()).thenReturn(4);
String result = create.call();
assertEquals("Hello World 4", result);
}
}
Use Mockito and inject a mock that you create. I would prefer constructor injection:
#RestController
public class Create {
private ComponentThatDoesSomething something;
#Autowired
public Create(ComponentThatDoesSomething c) {
this.something = c;
}
}
Don't use Spring in your Junit tests.
public CreateTest {
private Create create;
#Before
public void setUp() {
ComponentThatDoesSomething c = Mockito.mock(ComponentThatDoesSomething .class);
this.create = new Create(c);
}
}

How to combine Mockito and Spring in TestNG

We are building an application which uses Spring Boot. We write unit tests using TestNG and Mockito. However I find it pretty annoying to write when(...) configuration, I would like to use real components instead. I started to use #Spy components instead of mocks and this works pretty well until I need to put a Spy into a Spy. I'd like to avoid loading a Spring Context if possible, because creation of the context is very slow it looks like overkill for me to load it for at max 5 classes.
Is there any way, how could I use real code instead of Mocks and not loading whole Spring context? Or is my approach wrong at all and I should mock out all other classes then the tested one?
The other way to do this and may take some modifying of code on your end is to do it by constructor injection instead of field injection. Basically taking away any need of the spring context for testing. so the same from the other answer
Class to test
#Service
public class RecordServiceImpl implements RecordService
{
private final RecordRepository recordRepository;
#Autowired
public RecordServiceImpl(RecordRepository recordRepository)
{
this.recordRepository = recordRepository;
}
public Record find(String id)
{
return recordRepository.findOne(id);
}
public List<Record> findAll()
{
return recordRepository.findAll();
}
#Transactional
public Record save(Record record)
{
record.setRecordStatus("F");
return recordRepository.save(record);
}
}
Test Case
//#RunWith(SpringJUnit4ClassRunner.class)
//#ContextConfiguration(classes = {RecordServiceTestConfig.class})
public class RecordServiceTest
{
// #Autowired
private RecordRepository recordRepository = Mockito.mock(RecordRepository.class);
// #Autowired
private RecordService recordService;
#Before
public void setup()
{
Mockito.reset(recordRepository);
recordService = new RecordServiceImpl(recordRepository);
}
#Test
public void testFind()
{
Mockito.when(recordRepository.findOne(Mockito.anyString())).thenReturn(null);
Record record = recordService.find("1");
Assert.assertNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).findOne(Mockito.eq("1"));
}
#Test
public void testSave()
{
Mockito.when(recordRepository.save(Mockito.any(Record.class)))
.thenAnswer(new Answer<Record>()
{
#Override
public Record answer(InvocationOnMock invocation) throws Throwable
{
Record record = (Record) invocation.getArguments()[0];
Assert.assertEquals("F", record.getRecordStatus());
return record;
}
});
Record record = new Record();
record = recordService.save(record);
Assert.assertNotNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).save(Mockito.eq(record));
}
#Test
public void findAll()
{
Mockito.when(recordRepository.findAll()).thenReturn(new ArrayList<Record>());
List<Record> records = recordService.findAll();
Assert.assertNotNull(records);
Assert.assertEquals(0, records.size());
Mockito.verify(recordRepository, Mockito.times(1)).findAll();
}
}
I think your looking for like this with the use of #ContextConfiguration and #Configuration
Class to test
#Service
public class RecordServiceImpl implements RecordService
{
#Autowired
private RecordRepository recordRepository;
public Record find(String id)
{
return recordRepository.findOne(id);
}
public List<Record> findAll()
{
return recordRepository.findAll();
}
#Transactional
public Record save(Record record)
{
record.setRecordStatus("F");
return recordRepository.save(record);
}
}
Test Class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {RecordServiceTestConfig.class})
public class RecordServiceTest
{
#Autowired
private RecordRepository recordRepository;
#Autowired
private RecordService recordService;
#Before
public void setup()
{
Mockito.reset(recordRepository);
}
#Test
public void testFind()
{
Mockito.when(recordRepository.findOne(Mockito.anyString())).thenReturn(null);
Record record = recordService.find("1");
Assert.assertNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).findOne(Mockito.eq("1"));
}
#Test
public void testSave()
{
Mockito.when(recordRepository.save(Mockito.any(Record.class)))
.thenAnswer(new Answer<Record>()
{
#Override
public Record answer(InvocationOnMock invocation) throws Throwable
{
Record record = (Record) invocation.getArguments()[0];
Assert.assertEquals("F", record.getRecordStatus());
return record;
}
});
Record record = new Record();
record = recordService.save(record);
Assert.assertNotNull(record);
Mockito.verify(recordRepository, Mockito.times(1)).save(Mockito.eq(record));
}
#Test
public void findAll()
{
Mockito.when(recordRepository.findAll()).thenReturn(new ArrayList<Record>());
List<Record> records = recordService.findAll();
Assert.assertNotNull(records);
Assert.assertEquals(0, records.size());
Mockito.verify(recordRepository, Mockito.times(1)).findAll();
}
}
Test Class Configuration
#Configuration
public class RecordServiceTestConfig
{
#Bean
public RecordService recordService()
{
return new RecordServiceImpl();
}
#Bean
public RecordRepository recordRepository()
{
return Mockito.mock(RecordRepository.class);
}
}
the entire test class took 714ms to run the findAll test took 1ms.
If you are looking to configure your testcase using testng with Spring then you to mention
#ContextConfiguration(locations={
"/context.xml","/test-context.xml"})
at class level to load you spring file and extends your class org.springframework.test.context.testng.AbstractTestNGSpringContextTests
Sample
https://dzone.com/articles/spring-testing-support-testng

Categories