I have tried with #Autowired on the objectMapper, also tried to Mock it but no success, I just want to use the writeValueAsStringMethod so I do not have to pass a long json string to the content method below.
If I mark my class with #SpringBootTest and also #AutoconfigureMockMvc it works (the objectmapper is not null) but I believe that there must be another way so that it does not become mandatory to use this annotations.
TestClass:
#ExtendWith(MockitoExtension.class)
public class CarControllerTest {
private MockMvc mockMvc;
#InjectMocks
private CarController carController;
#Mock
private ObjectMapper objectMapper;
#MockBean
private CarParts carParts;
#BeforeEach
public void before() {
mockMvc = MockMvcBuilders.standaloneSetup(carController).build();
}
#Test
#DisplayName("Car Controller Test")
public void carControllerTest() {
try {
CarCustomRequest carCustomRequest = buildRequest();
ResultActions resultActions = mockMvc.perform(post("/custom/endpoint")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(carCustomRequest)));
MvcResult mvcResult = resultActions.andExpect(status().isOk()).andReturn();
assertTrue(mvcResult.getResponse().getStatus() == 200);
} catch (Exception e) {
fail("Error testing /custom/endpoint");
}
}
In order to #Autowire you need to load component classes that create the corresponding bean. To make tests faster you could define custom application context and load required beans only inter of using #SpringBootTest without params.
#SpringBootTest(classes = JacksonAutoConfiguration.class)
class ObjectMapperTest {
#Autowired
private ObjectMapper mapper;
#Test
void checkObjectMapper() {
assertNotNull(mapper);
}
}
I would not use #Mock in this case because it will be required to create stubs for required methods.
As an alternative, you could simply create a new instance of the ObjectMapper in test.
class ObjectMapperTest {
private ObjectMapper mapper = new ObjectMapper();
#Test
void checkObjectMapper() {
assertNotNull(mapper);
}
}
You could also register additional modules if required
objectMapper.registerModule(new JavaTimeModule());
Related
I am trying to add some tests to my project and I need to populate the DB before running my tests.
I have tried to do it by using a void setup() method, annotated with BeforeEach and trying to make sure that data is not rolled back. Here is my code sample. I would have to add that both shippingAddressRepository and billingAddressRepository extend JPARepository. When I run test, it throws a custom exception that tells me that id for shipping address is not found in DB.
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = {OrdersApiApplication.class, H2JpaConfig.class})
#AutoConfigureMockMvc
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#TestClassOrder(ClassOrderer.OrderAnnotation.class)
#TestPropertySource("classpath:application.yml")
class OrdersControllerTest extends GenericIntegrationTest {
#Autowired
private MockMvc mockMvc;
#Autowired
private ShippingAddressRepository shippingAddressRepository;
#Autowired
private BillingAddressRepository billingAddressRepository;
private static ShippingAddress testShippingAddress;
private static BillingAddress testBillingAddress;
#BeforeAll
#Rollback(value=false)
public void setup() {
testShippingAddress = shippingAddressRepository.save(new ShippingAddress("1","0722123443","Popescu","Ana","aleea lalelelor", "Romania","Sv","2112",null));
System.out.println(testShippingAddress);
testBillingAddress = billingAddressRepository.save(new BillingAddress("1", "0722123443","Popescu","Ana","aleea lalelelor", "Romania","Sv","2112",null));
}
#Nested
class CreateOrderTests {
#Nested
class CreateProductTests {
private ObjectMapper mapper = new ObjectMapper();
#Test
void shouldCreateOrderSuccessfully() throws Exception {
ShippingAddress shippingAddress = new ShippingAddress();
shippingAddress.setId("1");
// act
MvcResult mvcResult = mockMvc
.perform(createPostRequest(OrdersController.PATH, new CreateOrderReqDTO(OrderStatus.PAID, 3, 100.0, 100.0, 5.0, "1", "1", "1", "1")))
.andReturn();
OrderDTO response = mapper.readValue(mvcResult.getResponse().getContentAsString(), new TypeReference<>() {
});
// assert
assertEquals(100.0, response.getTotal());
assertEquals(100.0, response.getGrandTotal());
}
}
}
}
There are many ways:
use liquibase scripts with test data for test environment
use #Sql annotation https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/context/jdbc/Sql.html
If you use testcontainers like PostgreSQLContainer, there are method withInitScript("someScript.sql")
If you use H2 then put to your url init part like : "jdbc:h2:mem:test;INIT=RUNSCRIPT FROM 'classpath:scripts/create.sql'"
I have a spring service (MyService) that uses a mapstruct mapper (CustomMapstructMapper) :
#Service
public class MyService {
private final ClassA classA;
private final ClassB classB;
/*
other private fields declarations...
*/
private CustomMapstructMapper customMapstructMapper = Mappers.getMapper(CustomMapstructMapper.class);
//MyService constructor
public MyService(final ClassA classA, etc...) {
this.classA = classA;
//etc...
}
public ServiceOutput mainMethod(some parameters) {
//some business logic
MyMapperOutput myMapperOutput = customMapstructMapper.map(MapperParameter parameter);
ServiceOutput serviceOutput = some business logic with myMapperOutput;
return serviceOutput;
}
}
I want to unit test MyService (I am using Junit 5) and mock my CustomMapstructMapper output without calling the real mapper during the test execution. I already have another test class that specificly test all the custom mappings in CustomMapstructMapper.
So I have this test class :
#ExtendWith(MockitoExtension.class)
class MyServiceTest {
#InjectMocks
private MyService myService;
#Mock
private CustomMapstructMapper customMapstructMapper;
#Mock
private MyMapperOutput myMapperOutput;
#Test
void testMyService() {
/*
Some mocks creation ..
*/
Mockito.when(myMapperOutput.getSomeField()).thenReturn("Some value");
Mockito.when(customMapstructMapper.map(Mockito.any(MapperParameter.class))).thenReturn(myMapperOutput);
ServiceOutput serviceOutput = myService.mainMethod(some parameters);
/*
Some assertions on serviceOutput
*/
}
}
When I run my test, the implementation of my mapper customMapstructMapperImpl is called in MyService, not my mock.
A NullPointerException is thrown in my mapper because some fields are not initiated.
I have tried to create a mock with the implementation of my mapstruct mapper :
#Mock private CustomMapstructMapperImpl customMapstructMapper;
but I get the same result.
What am I doing wrong?
You're not taking advantage of the spring framework and mapstruct support for it.
in your service change:
private CustomMapstructMapper customMapstructMapper = Mappers.getMapper(CustomMapstructMapper.class);
into
#Autowired
private CustomMapstructMapper customMapstructMapper;
If you don't have it yet at your mapper use
#Mapper( componentModel = "spring" )
This will cause the generated mapper to have the #Component annotation from the spring framework, and it becomes possible to auto-wire this.
After making these changes your method of supplying a mock for testing should work.
The code below works correctly. So maybe something else happened. Your code example is not quite complete.
#ExtendWith(MockitoExtension.class)
public class TestForService {
#InjectMocks
private Service service;
#Mock
private Mapper mapper;
#Test
public void test_service() {
System.out.println("test_service start");
Mockito.when(mapper.doSomeTh()).thenReturn("mock mapper doSomeTh");
String result = service.service();
System.out.println("test_service end, result: " + result);
}
static class Service {
private Mapper mapper = Mapper.getMapper();
public String service() {
System.out.println("Service.service");
return mapper.doSomeTh();
}
public void setMapper(Mapper mapper) {
this.mapper = mapper;
System.out.println("Service.setMapper: " + mapper);
}
}
static class Mapper {
public String doSomeTh() {
System.out.println("Mapper.doSomeTh");
return "mapper end";
}
public static Mapper getMapper() {
System.out.println("Mapper.getMapper");
return null;
}
}
}
result:
Mapper.getMapper
Service.setMapper: mapper
test_service start
Service.service
test_service end, result: mock mapper doSomeTh
A simpler way here:
Just add a package-private setter method into your MyService
// for testing only
void CustomMapstructMapper setCustomMapstructMapper(CustomMapstructMapper mapper) {
this.customMapstructMapper = mapper;
}
and inject your mocked CustomMapstructMapper in test code
#Test
void testMyService() {
/*
Some mocks creation ..
*/
Mockito.when(myMapperOutput.getSomeField()).thenReturn("Some value");
Mockito.when(customMapstructMapper.map(Mockito.any(MapperParameter.class))).thenReturn(myMapperOutput);
myService.setCustomMapstructMapper(customMapstructMapper); // inject your mocked customMapstructMapper here
ServiceOutput serviceOutput = myService.mainMethod(some parameters);
/*
Some assertions on serviceOutput
*/
}
I am writing a simple test for a controller endpoint.
It works fine when I do the following.
#SpringBootTest
#ContextConfiguration(classes = {
HomeController.class,
HomeControllerTest.class
})
class HomeControllerTest {
#Autowired
private WebApplicationContext webApplicationContext;
private static final String URL = "/a";
private static final ObjectMapper objectMapper = new ObjectMapper();
#Test
public void test() throws Exception {
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
Request request = new Request();
mockMvc.perform(post(URL)
.contentType("application/json")
.content(objectMapper.writeValueAsString(request))
.andExpect(status().isOk());
}
}
But I do not want to have to create the mockMvc and concern with webApplicationContext.
Thus instead, attempting to use #AutoConfigureMockMvc instead as follows.
But this doesn't work. Fails with following error.
java.lang.AssertionError: Status expected:<200> but was:<403> Expected
:200 Actual :403
What am I doing wrong?
My attempt which throws above error.
#SpringBootTest
#AutoConfigureMockMvc // using this annotation instead
#ContextConfiguration(classes = {
HomeController.class,
HomeControllerTest.class
})
class HomeControllerTest {
// wiring mockMvc instead
// no webApplicationContext autowired
#Autowired
private MockMvc mockMvc;
private static final String URL = "/a";
private static final ObjectMapper objectMapper = new ObjectMapper();
#Test
public void test() throws Exception {
Request request = new Request();
mockMvc.perform(post(URL)
.contentType("application/json")
.content(objectMapper.writeValueAsString(request))
.andExpect(status().isOk());
}
}
Try create test like this exemple it is better.
#SpringBootTest
#AutoConfigureMockMvc
public class HomeControllerTest
private ObjectMapper mapper;
private MyControler myController;
private ServiceInSideConttroler service;
add your atributes
#Before
public init(){
this.mapper = new ObjectMapperConfiguration().mapper();
this.service = mock(ServiceInSideConttroler.class);
this.myController = new MyController(service);
}
#Test
public void test() throws Exception {
// exemple how mock reponse from any service or repository.
when(service.findById(any(Long.class)))
.thenReturn(Optional.of(budget));
Object resp = myController.save(mockben());
......
aserts(resp)
}
}
Remember, to work with the tests like this, do not use #Authwired in the attributes, only in the constructor of the classes annotated with #service, #componet, #controller etc .... that is, any class controlled by Spring that will use dependecia injection. Also rember asserts your reponse.
I've been trying to figure out why my mocked findIngredientsByCategory method is returning null when I have when(controller.findIngredientsByCategory(any()).thenReturn(Collections.emptyList()). This implementation works for the findAll method works.
Below is my implementation for my unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(IngredientController.class)
#ContextConfiguration(classes = {TestContext.class, WebApplicationContext.class})
#WebAppConfiguration
public class IngredientControllerTest {
#Autowired
private WebApplicationContext context;
#Autowired
private MockMvc mvc;
#MockBean
private IngredientController ingredientController;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mvc = MockMvcBuilders.webAppContextSetup(context).build();
}
#Autowired
private ObjectMapper mapper;
private static class Behavior {
IngredientController ingredientController;
public static Behavior set(IngredientController ingredientController) {
Behavior behavior = new Behavior();
behavior.ingredientController = ingredientController;
return behavior;
}
public Behavior hasNoIngredients() {
when(ingredientController.getAllIngredients()).thenReturn(Collections.emptyList());
when(ingredientController.getIngredientsByCategory(any())).thenReturn(Collections.emptyList());
when(ingredientController.getIngredientById(anyString())).thenReturn(Optional.empty());
return this;
}
}
#Test
public void getIngredientsByCategoryNoIngredients() throws Exception {
Behavior.set(ingredientController).hasNoIngredients();
MvcResult result = mvc.perform(get("/ingredients/filter=meat"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))
.andReturn();
String content = result.getResponse().getContentAsString();
System.out.println(content);
}
And below is the implementation for the controller:
#RestController
#RequestMapping("/ingredients")
public class IngredientController {
#Autowired
private IngredientRepository repository;
#RequestMapping(value = "/filter", method = RequestMethod.GET)
public List getIngredientsByCategory(#RequestParam("category") String category) {
return repository.findByCategory(category);
}
}
I'm not sure why the mock controller is returning null with this request, when I tell it to return an empty list. If someone could please help with this I would greatly appreciate it! Thanks.
Th request path in test is "/ingredients/filter=meat", but it should be "/ingredients/filter?category=meat". So, it seem that getIngredientsByCategory was not called.
The MockMvc actually will call the IngredientController that is bootstrapped and created by the Spring Test framework but not call the mocked IngredientController that you annotated with #MockBean, so all the stubbing that you made will not be called.
Actually, the point of #WebMvcTest is to test #RestController and its related Spring configuration is configured properly , so a real instance of IngredientController is necessary to create rather than using a mocked one. Instead , you should mock the dependencies inside IngredientController (i.e IngredientRepository).
So , the codes should looks like:
#RunWith(SpringJUnit4ClassRunner.class)
#WebMvcTest(IngredientController.class)
#ContextConfiguration(classes = {TestContext.class, WebApplicationContext.class})
#WebAppConfiguration
public class IngredientControllerTest {
#Autowired
private WebApplicationContext context;
#Autowired
private MockMvc mvc;
#MockBean
private IngredientRepository ingredientRepository;
#Test
public void fooTest(){
when(ingredientRepository.findByCategory(any()).thenReturn(Collections.emptyList())
//And use the MockMvc to send a request to the controller,
//and then assert the returned MvcResult
}
}
I try to write junit test to my service.
I use in my project spring-boot 1.5.1. Everything works fine, but when I try to autowire bean (created in AppConfig.class), it gives me NullPointerException. I've tried almost everything.
This is my configuration class:
#Configuration
public class AppConfig {
#Bean
public DozerBeanMapper mapper(){
DozerBeanMapper mapper = new DozerBeanMapper();
mapper.setCustomFieldMapper(new CustomMapper());
return mapper;
}
}
and my test class:
#SpringBootTest
public class LottoClientServiceImplTest {
#Mock
SoapServiceBindingStub soapServiceBindingStub;
#Mock
LottoClient lottoClient;
#InjectMocks
LottoClientServiceImpl lottoClientService;
#Autowired
DozerBeanMapper mapper;
#Before
public void setUp() throws Exception {
initMocks(this);
when(lottoClient.soapService()).thenReturn(soapServiceBindingStub);
}
#Test
public void getLastResults() throws Exception {
RespLastWyniki expected = Fake.generateFakeLastWynikiResponse();
when(lottoClient.soapService().getLastWyniki(anyString())).thenReturn(expected);
LastResults actual = lottoClientService.getLastResults();
Can someone tells me what's wrong ?
Error log:
java.lang.NullPointerException
at pl.lotto.service.LottoClientServiceImpl.getLastResults(LottoClientServiceImpl.java:26)
at pl.lotto.service.LottoClientServiceImplTest.getLastResults(LottoClientServiceImplTest.java:45)
and this is my service:
#Service
public class LottoClientServiceImpl implements LottoClientServiceInterface {
#Autowired
LottoClient lottoClient;
#Autowired
DozerBeanMapper mapper;
#Override
public LastResults getLastResults() {
try {
RespLastWyniki wyniki = lottoClient.soapService().getLastWyniki(new Date().toString());
LastResults result = mapper.map(wyniki, LastResults.class);
return result;
} catch (RemoteException e) {
throw new GettingDataError();
}
}
Ofcourse your dependency will be null,due to the #InjectMocks you are creating a new instance, outside the visibility of Spring and as such nothing will be auto wired.
Spring Boot has extensive testing support and also for replacing beans with mocks, see the testing section of the Spring Boot reference guide.
To fix it work with the framework instead of around it.
Replace #Mock with #MockBean
Replace #InjectMocks with #Autowired
Remove your setup method
Also apparently you only need a mock for the SOAP stub (so not sure what you need to mock for the LottoClient for).
Something like this should do the trick.
#SpringBootTest
public class LottoClientServiceImplTest {
#MockBean
SoapServiceBindingStub soapServiceBindingStub;
#Autowired
LottoClientServiceImpl lottoClientService;
#Test
public void getLastResults() throws Exception {
RespLastWyniki expected = Fake.generateFakeLastWynikiResponse();
when(soapServiceBindingStub.getLastWyniki(anyString())).thenReturn(expected);
LastResults actual = lottoClientService.getLastResults();