How to mock ObjectMapper with mokito? - java

I'm having trouble with mocking an ObjectMapper bean with mokito.
My class containing the ObjectMapper :
public class ServiceParent{
#Autowired
protected ObjectMapper objectMapper;
public void someMethod(){
...
Map<String, String> mapResponse = objectMapper.readValue("some json", new TypeReference<Map<String, String>>(){});
}
Other class extending previous one
public class ServiceChild extends ServiceParent{
...
}
My test class :
#SpringBootTest
public class TestService{
#Mock
ObjectMapper objectMapper;
#InjectMocks
ServiceChild serviceChild;
#Test
public void test(){
Map<String, String> mapResponse=new HashMap<String, String>();
mapResponse.put("access_token", "token_bouhon");
Mockito.when(objectMapper.readValue(Mockito.anyString(), Mockito.any(Class.class))).thenReturn(mapResponse);
}
So when I debug this, in ServiceParent, objectMapper isn't null but readValue(...) return null.
Do you have an idea on how to return the correct mocked object?
Thanks

I faced the same NPE with the objectmapper. This issue is resolved after adding ,
my test class has class level annotation with
#RunWith(MockitoJUnitRunner.class)
and the fix
#Before
public void setupBefore() {
MockitoAnnotations.initMocks(this);
}

#Mock creates a new mock. It's equivalent to calling mock(SomeClass.class)
#MockBean creates a mock, like #Mock, but also replaces any bean already in the application context with the same type with that mock. So any code which Autowires that bean will get the mock.
You need to use #MockBean

Related

Why tested class based autowire annotation throw null exception?

I use Spring Boot 5 and JUnit in my project. I create a unit test to test the service.
Here is the service that I am testing:
#Service
#RequiredArgsConstructor
#Slf4j
public class BuilderServiceImpl implements BuilderService{
#Autowired
public AutoMapper autoMapper;
private final BuilderRepository builderRepository;
private final AdminUserRepository adminUserRepository;
#Override
public BuilderDto getByEmail(String email){
}
#Override
public List<BuilderMinDto> getAll() {}
#Override
public List<BuilderMinDto> getAll(int page, int size) {}
#Override
public SaveBuilderResponse create(Builder builder){
var str = autoMapper.getDummyText();
Builder savedBuilder = builderRepository.save(builder);
return new SaveBuilderResponse(savedBuilder);
}
}
And here is the test class that tests the service above:
#SpringBootTest
#RequiredArgsConstructor
#Slf4j
class BuilderServiceImplTest {
#Mock
private BuilderRepository builderRepository;
#Mock
private AdminUserRepository adminUserRepository;
private AutoCloseable autoCloseable;
private BuilderService underTest;
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
#AfterEach
void tearDown () throws Exception{
autoCloseable.close();
}
#Test
void getByEmail(){}
#Test
#Disabled
void getAll() { }
#Test
#Disabled
void testGetAll() {}
#Test
void create() {
//given
Builder builder = new Builder();
builder.setName("John Johnson");
builder.setCompanyName("Builders Test");
builder.setEmail("test#builders.com");
//when
underTest.create(builder);
//then
ArgumentCaptor<Builder> builderArgumentCaptor = ArgumentCaptor.forClass(Builder.class);
verify(builderRepository)
.save(builderArgumentCaptor.capture());
Builder captureBuilder = builderArgumentCaptor.getValue();
assertThat(captureBuilder).isEqualTo(builder);
}
}
When I start to run the test class the create method in BuilderServiceImpl fired and on this row:
var str = autoMapper.getDummyText();
I get NullPointerException(autoMapper instance is null).
Here is the definition of AutoMapper class:
#Component
#Slf4j
#RequiredArgsConstructor
public class AutoMapper {
public String getDummyText(){
return "Hello From AutoMapper.";
}
}
As you can see I use #Component annotation to register the AutoMapper class to the IoC container and Autowired annotation to inject it into autoMapper property in BuilderServiceImpl class.
Why autoMapper instance is null? How can I make autoMapper to be initialized?
In order to make #Autowire work you have to use the instance of BuilderServiceImpl (object under test) created by spring itself.
When you create the object like this (by yourself, manually):
#BeforeEach
void setUp(){
....
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
}
Spring doesn't know anything about this object, hence Autowiring won't work
Another thing that might be useful:
You've used #Mock for BuilderRepository and AdminUserRepository.
This are plain mockito annotation, and if you're using an integration/system test that runs the spring under the hood, probably this is not what you want:
Surely, it will create a mock, but it won't put it onto an application context, and won't substitute the beans of these classes that might have been created by spring.
So if this is what you want to achieve, you should use #MockBean instead.
This annotation belongs to Spring Testing framework rather than a plain mockito annotation.
All-in-all you might end up with something like this:
#SpringBootTest
class MyTest
{
#MockBean
BuilderRepository builderRepo;
#MockBean
AdminUserRepository adminUserRepo;
#Autowired // spring will inject your mock repository implementations
// automatically
BuilderServiceImpl underTest;
#Test
void mytest() {
...
}
}
Add #Autowire annotation Annotations on below fields. Error due your not initialized below object In BuilderServiceImpl
#Autowire
private final BuilderRepository builderRepository;
#Autowire
private final AdminUserRepository adminUserRepository;
Why are you creating BuildService manually? If you do this, set AutoMapper manualy too.
#BeforeEach
void setUp(){
autoCloseable = MockitoAnnotations.openMocks(this);
underTest = new BuilderServiceImpl(builderRepository,adminUserRepository);
underTest.setAutoMapper(new AutoMapper());
}
You are not using di.

The service is set to null in my Junit Test

I am creating tests for my Springboot application. The application through a Get call, passing my "CarRequest" in the RequestBody
public class CarsRequest implements Serializable {
private String name;
private String plate ;
private String price;
}
it gives me back the car specifications related to that data
{
"name":"",
"plate":"",
"price":"",
"brand":"",
"kilometers":"",
"revisiondate":"",
"owner":""
}
I did this simple test using Mockito but I don't understand why my service is set by default to null, this throws everything in NullPointErexception
public class CarTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private CarService service;
#Autowired
ObjectMapper objectMapper;
#Test
public void TestOk() throws Exception{
CarsRequest carsRequest = new CarsRequest();
Car car = new Car();
List<Car> cars = new ArrayList<>();
//septum the fields of cars and add them to the list
cars.add(car);
Mockito.when(
service.getByPlate("bmw",
"TG23IO", "1500")).thenReturn(cars);
RequestBuilder requestBuilder = MockMvcRequestBuilders.get(
"/garage/cars").accept(
MediaType.APPLICATION_JSON);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
System.out.println(result.getResponse());
String expected = "{name:"bmw","plate":"TG23IO","price":"1500","brand":"POL","kilometers":"80000","revisiondate":"2016-03-15","owner":"JohnLocke"}";
JSONAssert.assertEquals(expected, result.getResponse()
.getContentAsString(), false);
}
}
Below I also add my CarService
#Service
public class CarService {
#Autowired
CarRepository carRepository;
#Autowired
ObjectMapper objectMapper;
public List<Cars> getByContratto(String name, String plate, String price) throws JsonProcessingException {
//some code, extraction logic
return cars;
}
}
The application works perfectly, only the test does not work. Being a novice in test writing I can't figure out what the null on my Carservice is due to.
If needed, I can include the Controller Get and the repository as well, but I don't think they can help
I believe you are trying to test the controller. Hence include the following annotations on top of the test class.
#SpringBootTest
#AutoConfigureMockMvc
#RunWith(SpringRunner.class)
Also, I can see that in the test code CarService is being referred while the service code that is shared contains DocumentService.
Try adding #RunWith(SpringRunner.class) to your test class.
I suspect that you are using Junit 4 for your tests. In Junit 4 you need to add
#RunWith(SpringRunner.class) in your test otherwise all annotations will be ignored.
For more info check the docs here . 46.3 Testing Spring Boot Applications is the section that answers your question.
I would however recommend you to migrate to Junit 5 if possible.
#RunWith(SpringRunner.class)
public class CarTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private CarService service;
#Autowired
ObjectMapper objectMapper;
#Test
public void TestOk() throws Exception{
// code here
JSONAssert.assertEquals(expected, result.getResponse()
.getContentAsString(), false);
}
}
Assuming, you want to test just only your controller layer (Because you are using mockMvc) and you are using Junit 4 (looks like from your code), you should write your test case as follows
#RunWith(SpringRunner.class)
#WebMvcTest(SomeController.class)
public class CarTest {
#MockBean
private SomeService service;
#Autowired
private MockMvc mockMvc;
#Test
public void shouldPassTest() {
BDDAssertions.assertThat(service).isNotNull();
}
}
Note: In example test method is irrelevant, I added just to to illustrate.
Let us know if it helps.

How to use constructor injection in Mapstruct's mapper?

In some mapper classes, I need to use an autowired ObjectMapper to transform String to JsonNode or verse-vera. I can acheive my goal by using the field injection with #autowired. But it's not suitable for unit test so I'd like to try using the constructor injection.
My current working code with the field injection:
#Mapper(componentModel = "spring")
public class CustomMapper {
#autowired
ObjectMapper mapper;
}
I try to convert it to the constructor injection so I can provide constructor argument in my unit test:
#Mapper(componentModel = "spring")
public class CustomMapper {
ObjectMapper mapper;
public CustomMapper(ObjectMapper mapper) {
this.mapper = mapper;
}
}
But I get a Constructor in CustomMapper cannot be applied to the given type error during compilation.
How do I fix it? Or is there other better way to map String to JsonNode in Mapstruct?
Constructor injection cannot be used in the mapper definition. Only in the mapper implementation.
However, for unit testing I'd suggest that you use setter injection.
Your mapper will then look like:
#Mapper( componentModel = "spring")
public class CustomMapper {
protected ObjectMapper mapper;
#Autowired
public void setMapper(ObjectMapper mapper) {
this.mapper = mapper;
}
}
1)The MapStruct has good feature:
#Mapper(componentModel = "spring", uses ={ObjectMapper.class}, injectionStrategy = InjectionStrategy.CONSTRUCTOR)
2)You could do it this way:
#Mapper(componentModel = "spring")
#RequiredArgsConstructor //lombok annotation, which authowire your field via constructor
public class CustomMapper {
private final ObjectMapper mapper;
}
But still you could do it via field.You stiil should mock it in tests in both cases. Just remember to use #InjectMocks
public CustomMapperTest {
#InjectMocks
private CustomMapper customMapper;
#Mock
private ObjectMapper objectMapper
#BeforeEach
void setUp() {
customMapper= new CustomMapperImpl();
MockitoAnnotations.initMocks(this);
when(objectMapper.map()).thenReturn(object);
}
#Test
void shouldMap() {
Object toObject = customerMapper.map(fromObject);
assertThat(toObject)
.hasFieldWithValue("fieldName", fromObject.getField());
}
}

AbstractClass Junit with Autowired annotation

I have an abstract class which uses #Autowired annotation inside it.
I am trying to write the junit using MockitoJUnitRunner.
#RunWith(MockitoJUnitRunner.class)
public class AbstractAdminSearchServiceTest {
#Mock
private IUPSService upsService;
Map<String,String> map;
#Before
public void setUp() {
map=new HashMap<>();
}
#Test
public void testSearchAdministratorsForIndividualNotification(){
AbstractAdminSearchService
mock=Mockito.mock(AbstractAdminSearchService.class,
Mockito.CALLS_REAL_METHODS);
when(upsService.getUsersProfile(buildUserIds(),new String[]
{})).thenReturn(map);
mock.searchAdministratorsForIndividualNotification(buildSolrUsers(),
"");
}
#Mock is not working and 'upsService' is not getting mocked.
As a result when actually upsService.getUsersProfile is called,i am getting NullpointerException.
Basically we will not write Junits for abstract classes, Because we can't create object for them,If it is a normal concrete class instead of
below code
mock=Mockito.mock(AbstractAdminSearchService.class,
Mockito.CALLS_REAL_METHODS);
use
#InjectMocks
private AbstractAdminSearchService mock;
and then all mocks will be inserted into real object

JUnit test Custom exception that throws #httpStatus

I would like to test that my custom exception is thrown when I call a method like that:
private MyObject queryDatabase(String request, String end){
HttpEntity myEntity = new NStringEntity(request, ContentType.APPLICATION_JSON)
Response response = restClient.performRequest("GET", end,Collections.emptyMap(), myEntity)
MyObject myObject = mapper.readValue(response.getEntity().getContent(), MyObject.Class);
if(myObject.getFunctions().isEmpty()){
throw new EmptyResultException("Empty result");
}
return myObject;
}
My EmptyResultException Class :
#ResponseStatus(value=HttpStatus.NO_CONTENT)
public Class EmptyResultException() extends RuntimeException(){
...
}
I'm just starting with JUnit and I have tried :
#Mock MyService myservice;
#Test(expected=EmptyResultException.class)
public void shouldReturnException() {
when(myService.queryDatabase(anyString(),anyString())).thenReturn(new MyObject());
}
It's not working. The behavior I want to try is in private method. It seems I should use Powermock for such cases. However, I have read it is not good to test private method so I would like to change my method to public. Any help please?
EDIT : My full implementation :
I have a Restcontroller :
#RestController
#RequestMapping...
public class MyController {
#Autowired
MyService myService;
#RequestMapping...
public MyObject findObject(#Valid DtoParameters parameters){
return myService.executeQuery(parameters);
}
}
And in myService Class :
#Service
public class MyService{
public MyObject executeQuery(DtoParameters parameters){
return
queryDatabase(parameters.buildRequest(),parameters.buildEnd());
}
}
If your class under test is MyService, it shouldn't have a #Mock.
I don't know your whole implementation, but let's assume MyService have injected dependencies (like your mapper)
First I would mock the mapper and inject it in MyService:
#Mock
private ObjectMapper mapper;
#InjectMocks
private MyService myService;
For this to work, your test class need to use the mockito runner. You can annotate your class with this:
#RunWith(MockitoJUnitRunner.class)
The mocks will be injected into MyService.
Now you can mock your mapper to return an empty object:
when(mapper.readValue(any(),any())).thenReturn(new MyObject());
myService.queryDatabase("request", "end")
when calling queryDatabase, this should call the mock inside.
If you were trying to test your rest controller, it is a different way. What I suggested is a unit test on MyService. If you want to test the controller, you should do it with a SpringRunner, SpringBootTest and MockMvc. But first, unit test your service, and add other tests on your controller later. For the controller, you would annotate MyService with #MockBean and throw, or Autowire it and #MockBean the mapper inside to return an empty object like the unit test.
Check this: https://spring.io/guides/gs/testing-web/
old answer:
You could change your method to protected, assuming your test is in the same package as the class you are testing, you will be able to mock it.
To mock your method, you need to write it like this:
when(myService.queryDatabase(anyString(),anyString())).thenThrow(EmptyResultException.class);
You forgot to use the when method to wrap your call.

Categories