Using Mockito to unit test Guice based class - java

My application uses Google Guice dependency injection framework. Im now having trouble figuring out a way to write unit tests for my class.
private final Person aRecord;
#Inject
public MyClass(Venue venue, #Assisted("record") Record myRecord) {
super(venue,myRecord);
aRecord = (Person) myRecord;
}
public void build() throws Exception {
super.build();
super.getParentRecord().setJobType(aRecord.getJobType());
super.getParentRecord().setHairColor(aRecord.getHairColor());
super.getParentRecord().setEyeColor(aRecord.getEyeColor());
}
I want to write a unit test for the build() method in the child class, it should
Ensure that when super.build() gets called all of the basic information on the super.getParentRecord() is populated e.g. age, gender etc.
Ensure an exception is thrown in the build() method if aRecord.getJobType() is null
Ensure an exception is thrown in the build() method if aRecord.getHairColor() is null
Ensure an exception is thrown in the build() method if aRecord.getEyeColor() is null

You have two dependencies of MyClass (Venue and Record) so you have to mock those.
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
...
Venue venueMock = mock(Venue.class);
Record recordMock = mock(Record.class);
Then in your unit test you have to create an instance of MyClass and assert the expected result:
For example: "Ensure an exception is thrown in the build() method if aRecord.getJobType() is null"
#Test(expected=RuntimeException.class)// or whatever exception you expect
public void testIfExceptionIsThrownWhengetJobTypeReturnsNull() throws Throwable {
Venue venueMock = mock(Venue.class); //create the mocks
Record recordMock = mock(Record.class);//create the mocks
when(recordMock.getJobType()).thenReturn(null); //specify the behavior of the components that are not relevant to the tests
MyClass myClass = new MyClass(venueMock, recordMock);
myClass.build();
//you can make some assertions here if you expect some result instead of exception
}
Note that if you don't specify the return value of any of the mocked dependencies (with when()) it will return the default value for the return type - null for objects, 0 for primitive numbers, false for boolean, etc. So it will be best to stub all of the methods that are used in MyClass.

Related

Mockito.mockConstruction does not return the mocked object

I don't want to use powermock anymore. Because junit5 started mocking static classes. So i am trying to get rid of powermock methods.
As you know, you can create an instance of a class with whenNew keyword. So i decided to use " mockConstruction " . But mockConstruction does not return the mocked object. It doesn't go inside the try block.
This is my BeforeEach method:
#BeforeEach
void setUp() {
partUnlinkService =
spy(new PartVideoUnlinkService(part1, part2,
part3));
}
This is my test method:
#Test
void shouldThrowException() throws Exception {
//given
UrlLinkDto urlLinkDto =
UrlPartLinkDto.builder().callId("333").videoId("5555544").build();
ArgumentCaptor<UrlPartLinkDto> argumentCaptor = ArgumentCaptor.forClass(UrlPartLinkDto.class);
//when
try (MockedConstruction<ObjectMapper> ignoredVariable = mockConstruction(ObjectMapper.class,
(objectMapper, context) -> {
//then
partUnlinkService.unlink(urlLinkDto, false);
verify(partLogCheckService, times(1)).checkForExistingVideo(
urlLinkDto.getVideoId());
verify(objectMapper, times(1)).writeValueAsString(argumentCaptor.capture());
Throwable throwable =
catchThrowable(() -> objectMapper.writeValueAsString(argumentCaptor.capture()));
assertThat(throwable).isInstanceOf(JsonProcessingException.class);
})) {
}
}
Any help would be appreciated.
You can access mocks that were created during the instantiation of your objects via MockedConstruction.constructed() method. It represents a collection of mocks that are created after each constructor's execution. If your object will be instantiated 3 times MockedConstruction<T>.constructed() will return 3 different mock objects for each instantiation.
According to documentation MockedConstruction<T>
Represents a mock of any object construction of the represented type.
Within the scope of the mocked construction, the invocation of any
interceptor will generate a mock which will be prepared as specified
when generating this scope. The mock can also be received via this
instance.
Simple implementation with comments below shows how to get mocks from MockedConstruction and verify them:
public class A {
private final String test;
public A(String test) {
this.test = test;
}
public String check() {
return "checked " + this.test;
}
}
public class TestService {
public String purchaseProduct(String param) {
A a = new A(param);
return a.check();
}
}
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedConstruction;
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
public class ConstructorMockTest {
private MockedConstruction<A> mockAController;
#BeforeEach
public void beginTest() {
//create mock controller for all constructors of the given class
mockAController = Mockito.mockConstruction(A.class,
(mock, context) -> {
//implement initializer for mock. Set return value for object A mock methods
when(mock.check()).thenReturn(" Constructor Mock A ");
});
}
#Test
public void test() {
//each instantiation of class A will return new mock, which initialized by initializer from beginTest method
//new mock will be stored to mockAController.constructed() collection of mocks
A aObject = new A("test");
//ensure that method check() returns mocked value
Assertions.assertEquals(aObject.check(), " Constructor Mock A ");
//get just created mock for class A from controller. It will be first element of mockAController.constructed() collection
A aMock = mockAController.constructed().get(0);
//ensure that we get correct mock from mock controller, that it is equal from new created object
Assertions.assertEquals(aMock, aObject);
//verify that check method was executed on Mock
verify(aMock, times(1)).check();
//create new A object, new mock created and stored to mockAController.constructed()
A aObject2 = new A("test");
//ensure that method check() returns mocked value
Assertions.assertEquals(aObject2.check(), " Constructor Mock A ");
//get just created mock for class A from controller, it will be second object from constructed collection
A aMock2 = mockAController.constructed().get(1);
//ensure that we get correct mock from mock controller, that it is equal from just created A object
Assertions.assertEquals(aObject2, aMock2);
//verify that check method was executed on Mock
verify(aMock2, times(1)).check();
//Example of testing service which creates A object
TestService service = new TestService();
String serviceResult = service.purchaseProduct("test");
//ensure that service returned value from A mock
Assertions.assertEquals(serviceResult, " Constructor Mock A ");
//get just created mock for class A from controller, it will be third object from constructed collection
A aMock3 = mockAController.constructed().get(2);
//verify that check method was executed on Mock
verify(aMock3, times(1)).check();
}
#AfterEach
public void endTest() {
mockAController.close();
}
}
Let's rewrite your test. I do not have the full code, but I will try to create an example with comments:
#Test
void shouldThrowException() throws Exception {
//given
UrlLinkDto urlLinkDto =
UrlPartLinkDto.builder().callId("333").videoId("5555544").build();
ArgumentCaptor<UrlPartLinkDto> argumentCaptor = ArgumentCaptor.forClass(UrlPartLinkDto.class);
try (MockedConstruction<ObjectMapper> objectMapperMockedConstruction = mockConstruction(ObjectMapper.class,
(objectMapper, context) -> {
//initialize ObjectMapper mock to throw exception when argumentCaptor will come to writeValueAsString method
doThrow(JsonProcessingException.class).when(objectMapper).writeValueAsString(argumentCaptor.capture());
})) {
//execute service for testing and catch exception
Throwable throwable = catchThrowable(() -> partUnlinkService.unlink(urlLinkDto, false));
//verify that inner service was executed
verify(partLogCheckService, times(1)).checkForExistingVideo(urlLinkDto.getVideoId());
//verify that ObjectMapper was instantiated
Assertions.assertEquals(1, objectMapperMockedConstruction.constructed().size());
//get mock of ObjectMapper which was instantiated during service execution
ObjectMapper objectMapper = objectMapperMockedConstruction.constructed().get(0);
//verify that writeValueAsString was executed
verify(objectMapper, times(1)).writeValueAsString(argumentCaptor.capture());
//check that exception is correct
assertThat(throwable).isInstanceOf(JsonProcessingException.class);
}
}
I think I have the same question as you, hope someone can solve our problem.
In my case, I also want to got a return object like PowerMock.whenNew().withArguments().thenReturn(someThing), I try a lot of times but it failed.
I think mockConstruction just only can mock their behavior, can't verify their result like powerMock, and I couldn't find any articles or answers.
below is my post link :
Junit 5 use mockConstruction().withSetting().useConstructor() instead of PowerMock.whenNew().withArguments()

How to mock #Value field using mockito

I am trying to test a method, but when my test method calls the actual method, due to #Value field present , the actual method always receives the value defined under #Value field i.e. null. You can have a look at the code for actual method and the test method below:
Actual method
public class IndexService {
#Value("${elasticsearch.index}")
private String index;
public boolean index(String id, String json, String index) {
try {
createIndex();
return true;
} catch (IOException e) {
log.warn("Exception in indexing data {}", e.getMessage());
}
return false;
}
private void createIndex() throws IOException {
CreateIndexRequest request = new CreateIndexRequest(index);
}
}
Below is my test method:
#Test
public void IndexServiceIndex() throws IOException {
CreateIndexRequest request1 = new CreateIndexRequest(index);
request1.source("{\"name\":true}",XContentType.JSON);
Mockito.when(indicesClient.create(request1,RequestOptions.DEFAULT))
.thenReturn(createIndexResponse);
Boolean indexser = indexService.index("65","{\"name\":molly}","1");
}
Below is CreateIndexRequest class method:
public CreateIndexRequest(String index) {
if (index == null) {
throw new IllegalArgumentException("The index name cannot be null.");
} else {
this.index = index;
}
}
What happening is, when my test method calls the actual method indexService.index("65","{\"name\":molly}","1");, then the control goes to the actual method, and the private method createIndex is injecting index value which is defined above as #Value("${elasticsearch.index}") private String index;. and hence in CreateIndexRequest method , it always evaluates to null and throws exception IllegalArgumentException("The index name cannot be null.").
I tried using ReflectionTestUtils.setField but the required dependency of org.springframework.test.util.ReflectionTestUtils is not there in my project. Is there any other way to mock #Value field?
You simply don't. Using fields injection in general is discauraged because it makes testing code more complicated than it can be. To test whatever you try to test, use either
Constructor injection - you can #Value on constructor params, and it will be possible to put test value via constructor
setter injection - annotate setter method with #Value. It will work exactly the same in the container, and how to use it in tests is obvious
Use #TestProperties - but this will fix the values for the whole test class
Use reflection - this allows you to even mutate final fields, however if it comes to AOP and proxies, this might not simply work
and probably many others. I think that 1 and 2 are the most viable methods.

How to Correctly mock an object with Mockito

I am trying to test the ClientDetails class below and trying to learn JUnit and Mockito at the same time.
// this is the class I'm trying to test
public class ClientDetails {
#Autowired private IApplicantService<Applicant> applicantService;
#Autowired private ClientDetailsHelpers helpers;
public FullClientDetails getClientDetails(String businessId, boolean isNewClient) {
FullClientDetails fcd = new FullClientDetails();
ClientParams params = new ClientParams();
params.setBusinessId(businessId);
ApplicantId ai = applicantService.retrieveApplicantIdFromBusinessId(params);
Long applicantId = ai.getApplicantId();
params.setApplicantId(applicantId);
Applicant applicant = applicantService.retrieveApplicantDetailsByHerdNumber(params);
helpers.validateRetrievedApplicant(applicant, isNewClient, businessId);
fcd.setApplicant(applicant);
// more method calls that get and assign objects to the fcd object
return fcd;
}
}
// ClientDetailsHelpers.java method that throws exception
public void validateRetrievedApplicant(Applicant applicant, boolean isNewClient, String businessId) {
if (applicant.getApplicantId() == null && !isNewClient) {
throw new ValidationSearchException(businessId);
}
}
// test class
#RunWith(MockitoJUnitRunner.class)
public class ClientDetailsTest {
private final static String BUSINESS_ID = "A1234567";
private final static Long APPLICANT_ID = null;
#InjectMocks
ClientDetails clientDetails;
#Mock ApplicantServiceImpl applicantService;
#Mock ClientDetailsHelpers helpers;
private ApplicantParams params;
private Applicant applicantWithInvalidId = new Applicant();
ApplicantId applicantId = new ApplicantId(APPLICANT_ID, BUSINESS_ID);
#Before
public void before(){
applicantWithInvalidId.setApplicantId(null);
}
#Test(expected = ValidationSearchException.class)
public void testGetFullApplicationDetails(){
when(applicantService.retrieveApplicantIdFromBusinessId(Mockito.any(ApplicantParams.class))).thenReturn(applicantId);
when(applicantService.retrieveApplicantDetailsByHerdNumber(Mockito.any(ApplicantParams.class))).thenReturn(applicantWithInvalidId);
FullClientDetails fcd = clientDetails.getFullClientDetails(BUSINESS_ID , false);
}
}
In my test class I create some mock objects, including an ApplicantId object to be returned when applicantService.retrieveApplicantIdFromBusinessId() is called and Applicant object with is applicantId attribute set to null to be return when applicantService.retrieveApplicantDetailsByHerdNumber() is called.
The function ClientDetailsHelper.validateRetrievedApplicant() should throw an exception if Applicant.getApplicantId() returns a null and if the boolean isNewClient is set to false however it doesn't seem to be happening in the test, it throws no exception and the #Test fails.
My best guess is that I am not using when().thenReturn() to correctly return the Applicant and ApplicantId objects I have created and instead another Applicant object is getting passed to validateRetrievedApplicant() and returning and applicantId of 0 when it gets to the validation method.
Any suggestions? Thanks!
Your code is not throwing an exception because there is nowhere in the code you are testing that throws that exception. I assume your exception is thrown within the ClientDetailsHelpers class but you are mocking this class so it will not call the actual code and so no exception will be thrown.
You need to think about what you want to test. Do you want to test the ClientDetails class in isolation as a unit? In which case you don't need to worry about the exception being thrown since its not part of the functionality of that class.
The second option is that you want to do more of an integration test where you pull in an actual ClientDetailsHelpers class but in order to do this you will need to include some configuration in your test to make sure that this bean is available to the test Spring context. You can do this using a Spring4JunitRunner instead of the Mockito one and then pulling in a configuration class with a component scan for your ClientDetailsHelpers class using the #ContextConfiguration(MyConfig.class) annotation on your test class where MyConfig is the relevant Spring config class.

Mockito when/then not returning expected value

I'm trying to stub this getKeyFromStream method, using the 'any' matchers. I've tried being more explicit and less explicit (anyObject()), but it seems like no matter what I try, this stub will not return the fooKey in my unit test.
I'm wondering if it is because it is protected or there is something else I'm missing or doing incorrectly. I have other when/then statements throughout the tests that are working but for some reason here, it is not.
Note: The getKeyFromStream generally uses a byteArrayInputStream, but I'm trying to match it with an InputStream, I've tried both to no avail.
public class FooKeyRetriever() //Mocked this guy
{
public FooKey getKey(String keyName) throws KeyException {
return getKeyFromStream(getKeyStream(keyName, false), keyName);
}
//Stubbed this method to return a key object which has been mocked
protected FooKey getKeyFromStream(InputStream keyStream, String keyName){
//Some code
return fooKey;
}
}
Unit Test
#Mock
private FooKeyRetriever mockKeyRetriever;
#Mock
private FooKey fooKey;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
#Test
public void testGetFooKey() throws Exception {
when(foo.getKeyFromStream(any(InputStream.class),any(String.class))).thenReturn(fooKey);
FooKey fooKey = mockKeyRetriever.getKey("irrelevant_key");
assertNotNull(fooKey);
}
The problem with your unit-test is, that you are trying to mock a method of your actual class which you want to test but you can't actually invoke a mock method as this will return null unless you declare a mocked return value on that invoked method. Usually, you only mock external dependencies.
There are actually two ways to create test-objects: mock and spy. The primer one will create a new object based on the class you provided which has internal state null and also return null on every invoked method. That's why you need to define certain return values for method invocations. spy on the other hand creates a real object and intercepts method invocations if "mock definitions" are defined for certain methods.
Mockito and PowerMock provide two ways of defining your mocked methods:
// method 1
when(mockedObject.methodToMock(any(Param1.class), any(Param2.class),...)
.thenReturn(answer);
when(mockedObject, method(Dependency.class, "methodToMock", Parameter1.class, Parameter2.class, ...)
.thenReturn(answer);
or
// method 2
doReturn(answer).when(mockedObject).methodToMock(param1, param2);
The difference is, that the method 1 will execute the methods implementation while the later one won't. This is important if you deal with spy objects as you sometimes don't want to execute the real code inside the invoked method but instead just replace the code or return a predefined value!
Although Mockito and PowerMock provide a doCallRealMethod() which you can define instead of doReturn(...) or doThrow(...), this will invoke and execute the code within your real object and ignores any mocked method return statements. Though, this is not that useful in your case where you want to mock a method of your class under test.
A method implementation can be "overwritten" by
doAnswer(Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
...
}
)
where you simply can declare what the logic of the invoked method should be. You can utilize this to return the mock result of the protected method therefore like this:
import static org.hamcrest.core.IsSame.sameInstance;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import java.io.InputStream;
import org.junit.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
public class FooKeyRetrieverTest {
#Test
public void testGetFooKey() throws Exception {
// Arrange
final FooKeyRetriever sut = spy(new FooKeyRetriever());
FooKey mockedKey = mock(FooKey.class);
doReturn(mockedKey)
.when(sut).getKeyFromStream(any(InputStream.class), anyString());
doAnswer(new Answer<FooKey>() {
public FooKey answer(InvocationOnMock invocation) throws Throwable {
return sut.getKeyFromStream(null, "");
}
}).when(sut).getKey(anyString());
// Act
FooKey ret = sut.getKey("test");
// Assert
assertThat(ret, sameInstance(mockedKey));
}
}
The code above works, however note that this has the same semantic as simply declaring a return value for the getKey(...) as
doReturn(mockedKey).when(sut).getKey(anyString());
Trying to modify only getKeyFromStream(...) with something like this:
doReturn(mockedKey)
.when(sut).getKeyFromStream(any(InputStream.class), anyString());
without modifying getKey(...) of your System-Under-Test (SUT) won't achieve anything as the real code of getKey(...) would be executed. If you however mock the sut-object, you could not invoke the method in your // Act section as this would return null. If you try
doCallRealMethod().when(sut).getKey(anyString());
on a mock object, the real method woulb be called and as mentiond beforehand, this would also invoke the real implementations of getKeyFromStream(...) and getKeyStream(...) regardless what you specified as mock-method.
As you probably can see by yourself, mocking methods of your actual class under test is not that useful and puts more burden to you than it provides any good. Therefore, it's up to you or your enterprise' policy if you want or need to test private/protected methods at all or if you stick to testing only the public API (which I would recommend). You also have the possibility to refactor your code in order to improve testability although the primary intent of refactoring should be to improve the overall design of your code.

Mockito UnfinishedStubbingException when trying to mock get class

I have a class LoggerInterceptor with an InvocationContext as parameter.
For this class I am trying to write a unit test but I am stuck on the first line:
public class LoggerInterceptor{
public method log(InvocationContext context) {
String name = invocationContext.getTarget().getClass().getName();
.....
}
My test looks like this:
#Test
public void logTest() throws Exception {
LoggerInterceptor objectToTest = new LoggerInterceptor();
InvocationContext context = Mockito.mock(InvocationContext.class);
Object target = Mockito.mock(Object.class);
Mockito.when(context.getTarget()).thenReturn(target);
MockGateway.MOCK_GET_CLASS_METHOD = true;
Mockito.doReturn(objectToTest.getClass()).when(target).getClass();
MockGateway.MOCK_GET_CLASS_METHOD = false;
objectToTest.log(context);
}
I am getting a UnfinishedStubbingException when I call the method log(context).
If I try with:
Mockito.when(target.getClass()).thenReturn(objectToTest.getClass());
I get this exception:
The method thenReturn(Class<capture#3-of ? extends Object>) in the type OngoingStubbing<Class<capture#3-of ? extends Object>> is not applicable for the arguments (Class<capture#4-of ? extends LoggerInterceptor>)
Is there any way I can pass this first line? The String that I get back is not important.
Object.getClass() is final. Final methods can't be mocked or stubbed with Mockito, because the compiler sees "final", skips the virtual method dispatch, and consequently Mockito can neither intercept calls to the method nor identify the method in doReturn, when, or verify. When you interact with Mockito later, it detects that you started stubbing but cannot detect that you finished, so it throws an UnfinishedStubbingException.
If you want to have target adjust the type or types it appears as, you'll need to change the class object passed into Mockito.mock, or add withSettings().extraInterfaces(...).

Categories

Resources