So, I am relatively new to unit-testing and especially mockito and am trying to figure out how to test the following scenario in Spring WebMVC:
This is my Service Class (simplified):
#Service
public class MyServiceImpl implements MyService {
#Resource
private MyCrudRepository myCrudRepository;
/**
* Method to delete(!) an entry from myTable.
*
*/
#Transactional
public void removeTableEntry(Long entryOid, String userId) throws Exception {
if (myCrudRepository.findOne(entryOid) != null) {
myCrudRepository.delete(entryOid);
log.info("User ID: " + userId + " deleted Entry from myTable with Oid " + entryOid + ".");
} else {
log.error("Error while deleting Entry with Oid: "+ entryOid + " from User with ID: " + userId);
throw new Exception();
}
}
}
Here I call the "built-in" delete-method of Spring Data JPA crudrepository, meaning no custom implementation in the repository itself (Using OpenJPA).
This is my simplified Test-Class:
#RunWith(MockitoJUnitRunner.class)
public class MyServiceImplTest {
final String USERID = "Testuser";
MyServiceImpl myService;
#Mock
MyCrudRepository myCrudRepository;
#Before
public void setUp() {
myService = new MyServiceImpl();
ReflectionTestUtils.setField(myService, "myCrudRepository", myCrudRepository);
}
//works (as expected? not sure)
#Test(expected = Exception.class)
public void testRemoveSomethingThrowsException() throws Exception {
doThrow(Exception.class).when(myCrudRepository).delete(anyLong());
myService.removeSomething(0l, USERID);
}
//does not work, see output below
#Test
public void testRemoveSomething() throws Exception {
verify(myCrudRepository, times(1)).delete(anyLong());
myService.removeSomething(0l, USERID);
}
//...
}
So, I try to verify that delete is called in testRemoveSomething(), but instead I get the following output:
Wanted but not invoked:
myCrudRepository.delete(<any>);
-> at myPackage.testRemoveSomething(MyServiceImplTest.java:98)
Actually, there were zero interactions with this mock.
And I'm nearly out of ideas why, to be honest (thinking about the #Transactional, perhaps? But this didn't get me to the solution, yet). May be that I'm completely wrong here (architectural, dunno) - if so, please feel free to give me a hint :)
It would be great to get some help here! Thanks in advance.
Your method fist calls findOne(), check if that returns something, and then calls delete(). So your test should first make sure that findOne returns something. Otherwise, the mock repository's findOne() method returns null by default. Moreover, you should verify that the call has been executed after it has been executed. Not before.
#Test
public void testRemoveSomething() throws Exception {
when(myCrudRepository.findOne(0L)).thenReturn(new TableEntry());
myService.removeTableEntry(0l, USERID);
verify(myCrudRepository, times(1)).delete(0L);
}
Also, you should use the #InjectMocks annotation rather than instantiating your service and injecting the repository using reflection.
Related
adding test cases for getStudent method, this is having internal calls
1- is repository call - stubbing this call working fine
2- validate user call - stubbing this call not working, showing some error and test case failed.
Service Class
#Service
public class StudentServiceImpl implements StudentService {
#Autowired
FakeStudentRepository fakeStudentRepository;
#Override
public Optional<Student> getStudent(int id) {
Optional<Student> student = fakeStudentRepository.getStudent(id);
boolean isValid = myClass().isValidUser(student.get().getId());
if(!isValid) {
return Optional.empty();
}
return student;
}
public MyTestClass myClass() {
return new MyTestClass();
}
}
MyTestClass
public class MyTestClass {
public boolean isValidUser(int id) {
return true;
}
}
Test Class
#SpringBootTest
class StudentServiceImplTest {
#Mock
FakeStudentRepository fakeStudentRepository;
#InjectMocks
StudentServiceImpl studentServiceImpl;
#BeforeEach
public void setup() {
studentServiceImpl = Mockito.spy(StudentServiceImpl.class);
MockitoAnnotations.initMocks(this);
}
#Test
void getStudent() {
Optional<Student> student = Optional.of(Student.builder().id(1).firstName("Rahul").lastName("rahul")
.mobile("XXXXXX").build());
Mockito.doReturn(student)
.when(fakeStudentRepository).getStudent(ArgumentMatchers.anyInt());
Mockito.doReturn(false)
.when(studentServiceImpl).myClass().isValidUser(ArgumentMatchers.anyInt());
Optional<Student> resultStudent = studentServiceImpl.getStudent(student.get().getId());
assertEquals(resultStudent.get().getId(), student.get().getId());
}
}
Error
org.mockito.exceptions.misusing.WrongTypeOfReturnValue: Boolean
cannot be returned by myClass() myClass() should return MyTestClass
If you're unsure why you're getting above error read on. Due to the
nature of the syntax above problem might occur because:
1. This exception might occur in wrongly written multi-threaded tests. Please refer to Mockito FAQ on limitations of concurrency
testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
The error Message says it: You are mocking studentServiceImpl.myClass() and try to return true. It’s not possible to mock the end of a call chain as you try with your second Mockito expression.
To do what you want requires to mock myClass() first by returning a mocked class instance and mock isValidUser on that.
I have a service class, with for readability purpose, I have provided the code with dummy variables and objects. I am trying to write a JUNIT test class for the service, primarily with Mockito. No matter how hard I try, I am not able to hit the method serviceMethod irrespective of using spy/mock. I have also included a test, following the main class.
I know I am missing something here, but doesn't cross my mind. I need an eye to review this and let me know how I can write a proper test class for this and obtain coverage for the method.
(P.S. all the necessary imports are in-place and not pasted here to keep this concise)
Thanks in advance!
#Service
public class ServiceClass {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceClass.class);
#Autowired
String stringUrl;
RestTemplate restTemplate = new RestTemplate();
public void serviceMethod(ModelObject model) {
try {
HttpEntity<ModelObject> request = new HttpEntity<>(model);
ResponseEntity<String> response = restTemplate.exchange(stringUrl,
HttpMethod.POST, request, String.class);
LOGGER.info(response.getBody() + "and " + response.getStatusCode());
} catch (HttpClientErrorException exception) {
LOGGER.info(exception.getResponseBodyAsString());
} catch (HttpStatusCodeException exception) {
LOGGER.info(exception.getResponseBodyAsString());
}
}
Sample Test:
#RunWith(MockitoJUnitRunner.Silent.class)
public class ServiceClassTest {
#InjectMocks
private ServiceClass serviceClass;
#Mock
private RestTemplate restTemplate;
#Test
public void testServiceMethod() {
ServiceClass spy = Mockito.spy(serviceClass);
// ServiceClass spy = mock(ServiceClass.class, Mockito.CALLS_REAL_METHODS);
doNothing().when(spy).serviceMethod(Mockito.any(ModelObject.class));
Mockito.doCallRealMethod().when(spy).serviceMethod(Mockito.any(ModelObject.class));
ResponseEntity<String> responseEntity = new ResponseEntity<>(HttpStatus.ACCEPTED);
Mockito.when(restTemplate.exchange(ArgumentMatchers.anyString(), ArgumentMatchers.any(HttpMethod.class),
ArgumentMatchers.<HttpEntity<ModelObject>>any(), ArgumentMatchers.<Class<String>>any()))
.thenReturn(responseEntity);
}
I have a class which I cannot easily use a dependency injection to mock due to an obligatory implementation of an interface - in a nutshell, for that reason I will be using Whitebox and my concern here is not related to the design, instead it is just to figure out how to properly "tearDown" the behavior caused by Whitebox. Bear with me for a second, I will give you more details - this is the main dummy class:
public class Dummy implements MandatoryInterface {
private static final Logger logger = Logger.getLogger(MethodHandles.lookup().lookupClass());
private final ObjectMapper mapper = new ObjectMapper();
#Override
public Object convertArgumentToJson(Object arg) {
if (arg != null) {
try {
return mapper.writeValueAsString(arg);
} catch (IOException e) {
// doSomething();
logger.error("Error tracking request", e);
}
}
return null;
}
}
Supposing that I want to cover what happens if an exception occurs here, the only way that I see is to use a Whitebox.setInternalState. Here the test:
public class DummyTest {
private Dummy dummy = new Dummy();
#Test
public void testA() throws IOException {
final ObjectMapper mock = Mockito.mock(ObjectMapper.class);
Whitebox.setInternalState(dummy, "mapper", mock);
Mockito.when(mock.writeValueAsString(Mockito.any()))
.thenThrow(new IOException());
Assert.assertNull(dummy.convertArgumentToJson("testA"));
}
#Test
public void testB() {
Assert.assertNotNull(dummy.convertArgumentToJson("testB"));
}
}
As you can see, I cannot define the mapper within the Dummy class as static, because of the Whitebox (it wouldn't work).
Having said that, after the execution of testA() we have the mapper being mocked:
The problem is: when executing the testB I don't want mock anymore - it should be the old instanced ObjectMapper initially included in Dummy. But what appears:
Now, my question:
What is the proper way to undo the
Whitebox.setInternalState(dummy, "mapper", mock);
P.S.: I have considered using a tearDown() like this:
#AfterMethod
public void tearDown(){
Whitebox.setInternalState(dummy, "mapper", originalState);
}
However, in this scenario, my pitest (mutation test) would consider that I'm not covering the initialization of ObjectMapper, so: is there a way just to undo the Whitebox for the rest of the tests without setting manually the old one?
Sorry for the long description and thanks in advance.
Regards,
Sorry guys, I managed to have it.
Just in case someone else could face the same question, the answer was easier than I supposed.
private static final String MAPPER_DESC = "mapper";
private ObjectMapper originalMapper;
#BeforeMethod
public void init() {
MockitoAnnotations.initMocks(this);
originalMapper = (ObjectMapper) Whitebox.getInternalState(converter, MAPPER_DESC);
}
#AfterMethod
public void tearDown() {
Whitebox.setInternalState(converter, MAPPER_DESC, originalMapper);
}
Then testA and testB can keep the same code. And the mutation test will still have the ObjectMapper attribute declaration covered as shown in the image:
In the application I'm working now, I need to implement a few tests and build upon them. I've been reading and trying out a few things, but haven't had much success.
The goal is to start back-filling with tests the service layer of the application. The first one to be covered is UserService.
So, my idea is to assert the test user we use on the application returns itself. The test class so far is:
#RunWith(MockitoJUnitRunner.class)
#SpringBootTest(classes = {ApplicationMain.class})
public class UserServiceTest {
#Mock
CentralData dataProviderMock;
#InjectMocks
private UserService userService;
private <project>User testUser;
private <project>User mockUser;
#Before
public void init() {
MockitoAnnotations.initMocks(CentralData.class);
System.out.println("dataProviderMock: " + dataProviderMock);
System.out.println("userService: " + userService);
userService = new UserService(dataProviderMock);
testUser = createTestUser();
}
private <project>User createTestUser() {
testUser = new <project>User();
testUser.setSystemId("testuser");
testUser.setEmailAddress("testuser#system.com");
testUser.setFirstName("Test");
testUser.setLastName("User");
// save user
userService.save(testUser);
return testUser;
}
#Test
public void whenUserIdIsProvided_thenRetrievedNameIsCorrect() {
mockUser = userService.getUserById("testuser");
when(userService.getUser("testuser")).thenReturn(testUser);
assertEquals(testUser, mockUser);
}
}
On my UserService, I have this:
public UserService(CentralData dataProvider) {
this.dataProvider = dataProvider;
}
When I save the user, the mocked dataProviderMock logs to the console what I expect it to log. But on the test itself, the mockUser is always null.
I understand the userService does not really accesses the data layer and the database, so mockUser being null is not really wrong. So, how could I perform this test?
I'm pretty sure I'm missing something quite basic here, but can't really see it.
I am new to netty framework.We have a API Handler implementing SimpleChannelInboundHandler and overriding the ChannelRead0 function that takes ChannelHandlerContext and the FullHTTPRequest.Now I need to do unit testing mocking the inputs.
Can anyone help me with this.
Lets assume I want to test my MyContentExtractionHandler, which looks like this:
public class MyContentExtractionHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
#Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception {
int contentLenght = msg.content().capacity();
byte[] content = new byte[contentLenght];
msg.content().getBytes(0, content);
ctx.fireChannelRead(new String(content));
}
}
I will create a regular DefaultFullHttpRequest and use mockito to mock a ChannelHandlerContext. My unit test would look like this:
public class MyContentExtractionHandlerTest {
#Mock
ChannelHandlerContext mockCtx = BDDMockito.mock(ChannelHandlerContext.class);
MyContentExtractionHandler myContentExtractorHandler = new MyContentExtractionHandler();
#Test
public void myTest() throws Exception {
String content = "MyContentHello";
DefaultFullHttpRequest fullHttpRequest = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, "/my /uri", Unpooled.copiedBuffer(content.getBytes()));
myContentExtractorHandler.channelRead(mockCtx, fullHttpRequest);
BDDMockito.verify(mockCtx).fireChannelRead(content); //verify that fireChannelRead was called once with the expected result
}
}
Most possibly, your SimpleChannelInboundHandler will be the final handler. So instead of checking for fireChannelRead() check for whatever method you call after reading the message.