I am trying to mock a very simple line of code that is used to query the DynamoDB using Java. Here is some sample code for the query -
List<Pojo> result;
try {
if (filters == null) {
this.queryExpression = new DynamoDBQueryExpression<Pojo>()
.withKeyConditionExpression(partitionKeyCondition)
.withExpressionAttributeValues(this.eav);
} else {
setFilterQueryExpression(filters);
}
result = this.dynamoDBMapper.query(Pojo.class, queryExpression);
} catch (final Exception e) {
throw new InternalServerException("Something went wrong with the database query: ", e);
}
The above piece of code works and I am able to retrieve a List of rows that automatically get deserialized into the Pojo.
I am now trying to Mock the this.dynamoDBMapper.query call as follows -
#Mock
private DynamoDBMapper mapper;
List<Pojo> result = new ArrayList<>();
when(mapper.query(Pojo.class,Mockito.any(DynamoDBQueryExpression.class)).thenReturn(result);
I am unable to do that with error -
Cannot resolve method 'thenReturn(java.util.List<com.amazon.xxx.xxx.Pojo>)'
I also tried another way -
doReturn(result).when(mapper).query(Pojo.class, Mockito.any(DynamoDBQueryExpression.class));
That seems to compile but the test fails with error -
org.mockito.exceptions.misusing.WrongTypeOfReturnValue
I have looked at other sample where the expected output of the query is of type PaginatedQueryList , I have tried changing to that as well. But I am still not sure why the above throws an error.
Do you also get the error when you use ArgumentMatchers?
Mockito.when(mapper.query(ArgumentMatchers.any(Pojo.class),ArgumentMatchers.any(DynamoDBQueryExpression.class)).thenReturn(result));
Do you also get the error if you expand the ArgumentMatchers (temporarily)?
Mockito.when(mapper.query(ArgumentMatchers.any(),ArgumentMatchers.any()).thenReturn(result));
As it turns out, you are missing a parenthesis before .thenReturn in order to complete the when part. Once you add it and switch from return type List to PaginatedQueryList, it should compile. Also note that any is a matcher. Once you specifiy a matcher, all arguments need to be matchers, therefore use eq etc. for your Pojo type. Otherwise, Mockito will show a InvalidUseOfMatchersException during runtime. Here is a simplified example that works for me:
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression;
import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedQueryList;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class MockTest {
#Test
void test() {
DynamoDBMapper mapperMock = mock(DynamoDBMapper.class);
PaginatedQueryList<String> expected = mock(PaginatedQueryList.class);
// Note that when needs to be completed before thenReturn can be called.
when(mapperMock.query(eq(String.class), Mockito.any(DynamoDBQueryExpression.class))).thenReturn(expected);
QueryService queryService = new QueryService(mapperMock);
PaginatedQueryList<String> actual = queryService.query();
assertEquals(expected, actual);
}
public class QueryService {
private final DynamoDBMapper mapper;
public QueryService(DynamoDBMapper mapper) {
this.mapper = mapper;
}
public PaginatedQueryList<String> query() {
DynamoDBQueryExpression<String> queryExpression = new DynamoDBQueryExpression<>();
return mapper.query(String.class, queryExpression);
}
}
}
Related
Apologies if I may be misunderstanding how Mockito works, but I'm trying to test for the case when one of my properties is null, setting it through mockito but it won't overwrite that unless I set the value for the whole object as null or make into a method and mock that instead and with the use of a spy.
Please let me try to better explain this with my code.
This is my class and the piece I want to test for. I'm checking the case where something wrong happens on saving a follower to a user so that is not set and returned empty. To be honest, I'm also not sure if it makes sense to test this with an if or whether I should be catching some exception here. But back to the original problem, the followerEntity object is always being created with the params for userTo and userFrom and this is what I'm trying to have mockito overwrite as null on the save call but in a way that it would return the other values for the user object. I'm not too sure, however, where I may be getting this wrong.
#Mock
UserRepository userRepository;
#Spy
#InjectMocks
UserServiceImpl userService;
#Override
public void followUser(UUID fromId, UUID toId) throws FollowerNotFoundException {
Optional<UserEntity> userEntityOptionalFrom = userRepository.findById(fromId);
Optional<UserEntity> userEntityOptionalTo = userRepository.findById(toId);
if (userEntityOptionalFrom.isEmpty() || userEntityOptionalTo.isEmpty()) {
throw new UserNotFoundException("No user found with this id");
}
UserEntity userEntityTo = userEntityOptionalTo.get();
UserEntity userEntityFrom = userEntityOptionalFrom.get();
Set<FollowingRequestEntity> followingRequestEntities = new HashSet<>();
FollowingRequestEntity followingRequestEntity = FollowingRequestEntity.builder().userSenderEntity(userEntityFrom).userReceiverEntity(userEntityTo).build();
followingRequestEntities.add(followingRequestEntity);
userEntityTo.setFollowedByEntity(followingRequestEntities);
userEntityTo = userRepository.save(userEntityTo);
if (userEntityTo.getFollowedByEntity() == null || userEntityTo.getFollowedByEntity().isEmpty()) {
throw new FollowerNotFoundException("Follower Not Found");
}
}
public UserEntity setFollower(UserEntity userEntityTo, UserEntity userEntityFrom) { // The tests work if calling and mocking this instead
Set<FollowingRequestEntity> followingRequestEntities = new HashSet<>();
FollowingRequestEntity followingRequestEntity = FollowingRequestEntity.builder().userSenderEntity(userEntityFrom).userReceiverEntity(userEntityTo).build();
followingRequestEntities.add(followingRequestEntity);
userEntityTo.setFollowedByEntity(followingRequestEntities);
return userEntityTo;
}
This is what I have for my test. As you can see, I've tried both when..Return() and doReturn() and also forcing the follower entity values to null, but when debugging my user object it always shows the FollowerBy property populated and not null.
#Test
void testFollowUser_ThrowsExceptionWhenFollowerIsFound() {
UUID userFromId = randomUUID();
UUID userToId = randomUUID();
UserEntity userEntityFrom = getUserEntity();
userEntityFrom.setId(userFromId);
UserEntity userEntityTo = getUserEntity();
userEntityTo.setId(userToId);
userEntityTo.setName("new name");
when(userRepository.findById(userEntityFrom.getId())).thenReturn(Optional.of(userEntityFrom));
when(userRepository.findById(userEntityTo.getId())).thenReturn(Optional.of(userEntityTo);
// when(userRepository.save(userEntityTo)).thenReturn(userEntityTo);
userEntityTo.setFollowerOfEntity(null);
userEntityTo.setFollowedByEntity(null);
doReturn(userEntityTo).when(userRepository).save(any());
// when(userEntityTo.getFollowedByEntity()).thenReturn(null);
// doReturn(userEntityTo).when(userService).setFollower(any(), any());
FollowerNotFoundException exception =
assertThrows(FollowerNotFoundException.class, () -> userService.followUser(userFromId, userToId));
assertEquals("Follower Not Found", exception.getMessage());
}
Thank you very much.
Here's a complete example (compressed into a single file) which explains what I have been trying to get across in the comments above:
package com.example.demo;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
class MyService {
private final MyRepository myRepository;
MyService(MyRepository myRepository) {
this.myRepository = myRepository;
}
public String doWork() {
return myRepository.get().toUpperCase();
}
}
class MyRepository {
public String get() {
return "a real thing from the DB";
}
}
#ExtendWith(MockitoExtension.class)
public class MyTests {
#Mock
MyRepository myRepository;
#InjectMocks
MyService myService;
#Test
public void aTest() {
when(myRepository.get()).thenReturn("something fake");
assertEquals("SOMETHING FAKE", myService.doWork());
}
}
The actual non-test code (which will live in src/main/java) is MyService and MyRepository. These classes have no reference to any test code.
MyTests is in src/test/java. It's the only part of your code which knows about mocks.
We can remove some of the "magic" by explicitly creating and injecting the mocks:
...
MyRepository myRepository;
MyService myService;
#BeforeEach
public void setup() {
myRepository = mock(MyRepository.class);
myService = new MyService(myRepository);
}
...
Looking at the function you are testing we see:
followingRequestEntities.add(followingRequestEntity);
userEntityTo.setFollowedByEntity(followingRequestEntities);
userEntityTo = userRepository.save(userEntityTo);
if (userEntityTo.getFollowedByEntity() == null || userEntityTo.getFollowedByEntity().isEmpty()) {
throw new FollowerNotFoundException("Follower Not Found");
}
So as followingRequestEntities always contains one entry, userEntityTo.getFollowedByEntity().isEmpty() will always be false, no matter what your mocks return, and the exception can never be thrown.
You can use:
when(userRepository.findById(userEntityFrom.getId())).thenReturn(Optional.empty());
and expect that UserNotFoundException will be thrown.
Hi I am using following code to print logs using aop in my webflux app,I have trouble writing unit/integration tests ?can we verify log interactions here?Any help would be appreciated
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Loggable {}
#Aspect
#Slf4j
public class LoggerAspect {
#Around("#annotation(Loggable)")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
var result = joinPoint.proceed();
if (result instanceof Mono) {
var monoResult = (Mono) result;
AtomicReference<String> traceId = new AtomicReference<>("");
return monoResult
.doOnSuccess(o -> {
var response = "";
if (Objects.nonNull(o)) {
response = o.toString();
}
log.info("Enter: {}.{}() with argument[s] = {}",
joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(),
joinPoint.getArgs());
log.info("Exit: {}.{}() had arguments = {}, with result = {}, Execution time = {} ms",
joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(),
joinPoint.getArgs()[0],
response, (System.currentTimeMillis() - start));
});
}
}
}
Test Failing.
Somehow when i debug pointer is not going inside doOnNext Method.And I am not sure how I can assert log interaction in above Logging aspect.In Junit5 I know I can use mockito for each method and return something ,but how i can rerturn in spock.
class LogAspectTest extends Specification {
private static final String MOCK_METHOD_LOG_VALUE = "mockMethodLogValue"
private Logger log = Mock()
private ProceedingJoinPoint mockJoinPoint = Mock()
private static Mono<String> methodReturn = Mono.just(["Data", "Data"])
private LogAspect logAspect = new LogAspect(log)
#Unroll
def 'logAround verify log interaction'() {
given:
mockJoinPoint.proceed() == Mono.just("Hello")
final Method method = TestClass.class.getMethod("mockMethod")
when:
logAspect.logAround(mockJoinPoint)
then:
interaction { mockJoinPointAndMethodSignatureInteractions(method, methodReturnToUse) }
where:
resultType | methodReturnToUse
'Mono' | methodReturn
}
private void mockJoinPointAndMethodSignatureInteractions(Method method, Publisher result) {
1 * mockJoinPoint.proceed() >> result
1 * log.info() >> ""
}
private static class TestClass {
#Loggable
Mono<String> mockMethod() { return Mono.just("data") }
}
}
Is it recommended to write Integration Test for #Loggable annotation since it just logging not sure how can write Integration Test which assert the log statements
Like I said in my comment, you cannot easily mock a private static final field without using add-on tools like PowerMock or similar. I think that whenever you need something like that, you should rather refactor your code for better testability. Here is an idea which is far from perfect, but I want to give you an idea about how you could unit-test your aspect. As for an integration test, you can also do that, but ask yourself what you want to test: really the aspect or that Spring AOP pointcut matching works correctly?
Anyway, let us assume that your classes under test are:
package de.scrum_master.stackoverflow.q64164101;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Loggable {}
package de.scrum_master.stackoverflow.q64164101;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Mono;
import java.util.Objects;
import java.util.function.Consumer;
#Aspect
public class LogAspect {
private static final Logger log = LoggerFactory.getLogger(LogAspect.class.getName());
#Around("#annotation(Loggable)")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed();
if (result instanceof Mono)
return ((Mono) result).doOnSuccess(getConsumer(joinPoint, start));
return result;
}
public Consumer getConsumer(ProceedingJoinPoint joinPoint, long start) {
return o -> {
String response = "";
if (Objects.nonNull(o))
response = o.toString();
log.info("Enter: {}.{}() with argument[s] = {}",
joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(),
joinPoint.getArgs());
log.info("Exit: {}.{}() had arguments = {}, with result = {}, Execution time = {} ms",
joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(),
joinPoint.getArgs()[0],
response, (System.currentTimeMillis() - start));
};
}
}
See how I factored out the lambda into a helper method? It has two effects:
It makes the logAround(ProceedingJoinPoint) advice method more readable.
It permits you to stub the helper method and instead of verifying that logging is done you just verify that the helper method was called for Mono results (and not called for other result types).
The test in its simplest form could look like this:
package de.scrum_master.stackoverflow.q64164101
import org.aspectj.lang.ProceedingJoinPoint
import reactor.core.publisher.Mono
import spock.lang.Specification
class LogAspectTest extends Specification {
LogAspect logAspect = Spy()
ProceedingJoinPoint joinPoint = Mock()
def "aspect target method returns a Mono"() {
given:
joinPoint.proceed() >> Mono.just("Hello")
when:
logAspect.logAround(joinPoint)
then:
1 * logAspect.getConsumer(joinPoint, _)
}
def "aspect target method does not return a Mono"() {
given:
joinPoint.proceed() >> "dummy"
when:
logAspect.logAround(joinPoint)
then:
0 * logAspect.getConsumer(joinPoint, _)
}
}
Please note how I use a Spy (i.e. a partial mock based on the original object) in order to selectively stub the helper method.
Update: An alternative for more integrative testing would be to configure your logging framework to log into a target which you can control and verify, e.g. log into an in-memory database or into a buffer which you can access.
The following is my test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/ds-context.xml")
#WebAppConfiguration
public class PaidListTest {
#Autowired
PaymentService paymentService;
#Test
public void getPaidList() {
List<PaymentGetServiceDO> response = null;
try {
response = paymentService.setPaidStatusList();
if(response != null && response.size() > 0){
for(int i = 0; i < response.size(); i++){
assertNotNull(response.get(i).getAgentcode());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
In the DAO layer of the service call paymentService.setPaidStatusList() have some operations with database activities of save and update, like em.merge(renewalPoliciesDO); But I don't want to execute them while calling the test method, they needs to get called only when actual business logic is called. How can I restrict or rollback the database transactions here?
The service and DAO methods are tedious here. However, I have simplified them for your reference. Service method
if(!updateList.isEmpty()){
HashMap<String,String> recordset = new HashMap<String,String>();
recordset = paymentDAO.setRenewalStatus(updateList);
}DAO implementation
if(paymentUpdateResDO.getPaymentstatus().equalsIgnoreCase("MANUAL") &&
!responseUpdateStatus.getPolicystatusid().equals(renewedStatusId)){
CriteriaBuilder criteriaBuilder = em.getCriteriaBuilder();
CriteriaQuery<Integer> payQuery = criteriaBuilder.createQuery(Integer.class);
Root<PaymentStatusDO> payRoot = payQuery.from(PaymentStatusDO.class);
payQuery.multiselect(payRoot.get("paymentstatusid"));
payQuery.where(criteriaBuilder.equal(payRoot.get("paymentstatusdescription"), "Manual"));
Integer paymentStatusId = em.createQuery(payQuery).getSingleResult();
insertOldPolicy(responseUpdateStatus);
responseUpdateStatus.setNewpolicyno(paymentUpdateResDO.getNewpolicyno());
responseUpdateStatus.setPreviousstatusid(responseUpdateStatus.getPolicystatusid());
responseUpdateStatus.setPolicystatusid(renewedStatusId);
Timestamp modifiedDate = new Timestamp(System.currentTimeMillis());
responseUpdateStatus.setModifieddatetime(modifiedDate);
responseUpdateStatus.setModifiedby("RPA");
responseUpdateStatus.setPaymentstatusid(paymentStatusId);
responseUpdateStatus.setActiveindicator("Y");
em.merge(responseUpdateStatus);
successRecords++;
}
In my case, I need the result arraylists, but the em.merge and em.persist activities need to be ignored.
When I try with MockitoJUnitRunner as #GauravRai1512 preferred, I get my testcase executed but the program is terminated so that I am unable to get the result ArrayLists.
Refer this image
You should follow below approach as suggested by Pooja Aggarwal.
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
public class PaidListTest {
#Mock
PaymentService paymentService;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mapperObj = new ObjectMapper();
List<PaymentGetServiceDO> response = new ArrayList<PaymentGetServiceDO>();
}
#Test
public void getPaidList() {
List<PaymentGetServiceDO> response = null;
try {
response = paymentService.setPaidStatusList();
if(response != null && response.size() > 0){
for(int i = 0; i < response.size(); i++){
assertNotNull(response.get(i).getAgentcode());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
This code will not execute your save and update operation.
You can use mocking of the database calls. You can write test cases using Jmockit where you can mock the call of database which will prevent save or update operation in the database.
You can learn writing jmockit test cases from http://jmockit.github.io/tutorial/Introduction.html
Or
https://winterbe.com/posts/2009/08/18/introducing-jmockit/
Like others have said, you can either:
Mock the PaymentService so that you can define exactly what to do on each of the calls to it. In this case, you would simply return a hard-coded List<PaymentGetServiceDO> response, and your test could do something with that. The downside of this is that you're not really testing PaymentService in that case. You're simply looking at hard-coded data, and doing something with it. In my view, that's not a valid test.
Or, you can instantiate a PaymentService object with either an in-memory database, only for use with tests, or with a mock DB object, where you can then define exactly how to respond to each of the merge, save, update and similar operations. If the PaymentService object doesn't allow this, then it should be redesigned so it accepts as a parameter a Database/Connection, which would be the normal #Autowired injected parameter when running in production, but which you can instantiate manually when testing. This would be a proper test, since you'd be validating the rest of the logic inside setPaidStatusList, but you're not actually hitting a database to achieve that.
Consider the following field and method from a class i need to test.
private final static String pathToUUID = "path/to/my/file.txt";
public String getUuid () throws Exception {
return new String(Files.readAllBytes(Paths.get(pathToUUID)));;
}
The UUID is stored in a file that is created on the application's first run. A file.txt exists in the location indicated by pathToUUID. I am trying (and struggling) to write a unit test for this method.
#RunWith(PowerMockRunner.class)
#PrepareForTest({Files.class})
public class MyTest {
private final String expected = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
#Test
public void testGetUuid() throws Exception {
UUIDGetter getter = new UUIDGetter();
PowerMockito.mockStatic(Files.class);
when(Files.readAllBytes(any(Path.class)).thenReturn(expected.getBytes());
String retrieved = getter.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}
Unfortunately when().thenReturn() is not called during testing and the test performs as an integration test, reading the file from the file system and returning its value, rather simply than the mock value i expect. However, if i spoof a call to Files.readAllBytes() in the test method and echo the result to the console, the expected value displays.
So, how can i get my method under test to properly function with the PowerMock when()-thenReturn() pattern?
For anyone facing a similar problem, i solved this by making the following changes to my test class:
#RunWith(PowerMockRunner.class)
#PrepareForTest({UUIDStasher.class})
public class TestUUIDStasher {
private final String expectedUUID = "19dcd640-0da7-4b1a-9048-1575ee9c5e39";
Path spoofPath = Paths.get("C:\\DIRECTORY");
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(Paths.class);
PowerMockito.mockStatic(Files.class);
when(Paths.get(any(String.class))).thenReturn(spoofPath);
when(Files.readAllBytes(any(Path.class))).thenReturn(expectedUUID.getBytes());
}
#Test
public void testGetUUID() throws Exception {
UUIDStasher stasher = new UUIDStasher();
String retrieved = stasher.getUuid();
Assert.assertEquals(expectedUUID, retrieved);
}
}
Your class that you need to test is written in a bad way. The path shouldn't be hard coded - make it parametrizable - for example inject the path via the constructor. Then, in your integration tests just inject the path to your test resources and you're ready to go. No PowerMock, no hacks - simple constructor injection.
JDK classes are hard to deal with when using PowerMock. Here's what I would do in your case:
Refactor UUIDGetter to add a constructor for testing purposes that accepts the path to the "uuid" file:
package so37059406;
import java.nio.file.Files;
import java.nio.file.Paths;
public class UUIDGetter {
private final static String PATH_TO_UUID = "path/to/my/file.txt";
private final String path;
public UUIDGetter() {
this(PATH_TO_UUID);
}
// for testing purposes
protected UUIDGetter(final String path) {
this.path = path;
}
public String getUuid() throws Exception {
return new String(Files.readAllBytes(Paths.get(this.path)));
}
}
then test it like this:
package so37059406;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class UUIDGetterTest {
#Test
public void testGetUuid() throws Exception {
final UUIDGetter getter = new UUIDGetter(getClass().getClassLoader().getResource("so37059406/uuid.txt").getPath());
assertEquals("19dcd640-0da7-4b1a-9048-1575ee9c5e39", getter.getUuid());
}
}
With a resource file (in test resources folder) named "so37059406/uuid.txt" and containing (no end-of-line):
19dcd640-0da7-4b1a-9048-1575ee9c5e39
This is IMHO, way better, because:
No powermock: it's a powerful tool but it comes with a price (slower tests, possible tests strange interactions
It's more readable / easy to understand
I started to use Guava Optional as a part of the null object pattern and would like to improve the use in Mockito, where null is the default return value for mocked objects. To behave correctly one needs to explicitly tell Mockito to use Optional.absent() instead:
import org.mockito.*;
import org.testng.*;
import org.testng.annotations.*;
import com.google.common.base.Optional;
public class Example {
#Mock
private MyObject underTest;
#Test
public void testExample() {
// fails
// assertNotNull(underTest.get());
Mockito.when(underTest.get()).thenReturn(Optional.absent());
Assert.assertNotNull(underTest.get());
}
public class MyObject {
public Optional<Object> get() {
return Optional.absent();
}
}
#BeforeClass
public void beforeClass() {
MockitoAnnotations.initMocks(this);
}
}
Is there a easy way to improve mockito to automatically return Optional.absent() instead of null if the actual type is Optional?
I tried to solve it with reflection in #Before annotated method, however, I didn't manage to get it working.
Solution you found out can be improved by creating your own static factory that creates mocks with an OptionalAnswer and use it instead of default Mockito factory:
class MockitoOptional{
public static <T> T mock(Class<T> classToMock) {
return Mockito.mock(classToMock, new OptionalAnswer());
}
}
Next step will be to extend a test runner that will use this factory to inject mocks into #Mock annotated fields. Search for custom JUnit test runners if you haven't heard of them yet.
I got a first shot with the linked answer for strings.
public class OptionalAnswer extends ReturnsEmptyValues {
#Override
public Object answer(InvocationOnMock invocation) {
Object answer = super.answer(invocation);
if (answer != null) {
return answer;
}
Class<?> returnType = invocation.getMethod().getReturnType();
if (returnType == Optional.class) {
return Optional.absent();
}
return null;
}
}
#Test
public void testExample() {
MyObject test = mock(MyObject.class, new OptionalAnswer());
Assert.assertNotNull(test.get());
}
Won't get much easier, right?
Mockito.when(underTest.get()).thenReturn(Optional.<Object>absent());
This is all you need to do. Add the type returned from underTest.get() to your absent() call. Yes it is supposed to be on that side of the period.