I wanted to write unit test for my addTask method with mockito.
Here is the class that contains this method.
package controller;
import model.Task;
import model.User;
import repository.TaskActions;
import repository.UserActions;
import java.sql.SQLException;
import java.util.List;
public class ToDoEngine {
private TaskActions taskActions;
private UserActions userActions;
private User connectedUser;
public ToDoEngine(UserActions userStorage, TaskActions taskStorage) {
this.taskActions = taskStorage;
this.userActions = userStorage;
}
public boolean signIn(String username, String password) throws SQLException {
connectedUser = new User(username, password);
if (!userActions.signIn(connectedUser)) {
return false;
}
connectedUser.setID(retrieveConnectedUserID(connectedUser));
return true;
}
private int retrieveConnectedUserID(User connectedUser) throws SQLException {
return userActions.retrieveUserID(connectedUser);
}
public void addTask(String taskName) throws SQLException {
taskActions.addTask(new Task(taskName), connectedUser);
}
}
Here are my attempts. Unfortunately, I've got error. Below, I am gonna present you stacktrace:
package controller;
import model.Task;
import model.User;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import repository.TaskActions;
import repository.UserActions;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class ToDoEngineTest {
#Mock
TaskActions taskActionsMock;
#Mock
UserActions userActionsMock;
private ToDoEngine toDoEngine;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
toDoEngine = new ToDoEngine(userActionsMock, taskActionsMock);
}
#Test
public void addTask() throws SQLException {
Task taskName = new Task("wash");
User user = new User("admin","123");
toDoEngine.addTask("wash");
verify(taskActionsMock).addTask(taskName,user);
}
}
stacktrace:
Argument(s) are different! Wanted:
taskActionsMock.addTask(
Task(taskName=wash),
model.User#1b71f500
);
-> at controller.ToDoEngineTest.addTask(ToDoEngineTest.java:68)
Actual invocation has different arguments:
taskActionsMock.addTask(
Task(taskName=wash),
null
);
-> at controller.ToDoEngine.addTask(ToDoEngine.java:40)
Comparison Failure: <Click to see difference>
Argument(s) are different! Wanted:
taskActionsMock.addTask(
Task(taskName=wash),
model.User#1b71f500
);
-> at controller.ToDoEngineTest.addTask(ToDoEngineTest.java:68)
Actual invocation has different arguments:
taskActionsMock.addTask(
Task(taskName=wash),
null
);
-> at controller.ToDoEngine.addTask(ToDoEngine.java:40)
at controller.ToDoEngineTest.addTask(ToDoEngineTest.java:68)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
...
User and Task class contains hashCode and Equals method.
Task
package model;
import lombok.*;
#Getter
#Setter
#AllArgsConstructor
#ToString
#NoArgsConstructor
#EqualsAndHashCode
public class Task {
private String taskName;
}
User:
package model;
import lombok.*;
#RequiredArgsConstructor
#Getter
#Setter
#EqualsAndHashCode
public class User {
private final String name;
private final String password;
private int ID;
}
Thanks in advance for help. :D
In your test case you are attempting to verify a call onto this method:
public void addTask(String taskName) throws SQLException {
taskActions.addTask(new Task(taskName), connectedUser);
}
With this:
User user = new User("admin","123");
...
verify(taskActionsMock).addTask(taskName,user);
The failure message ...
Actual invocation has different arguments:
taskActionsMock.addTask(
Task(taskName=wash),
null
);
... tells us that the value of connectedUser in the test call is null.
Looking at your code the connectedUser member of ToDoEngine is populated by a call to the signIn() method but your test case is not invoking that method and hence connectedUser is null when addTask is invoked by your test.
So, if you don't need/want to test that the correct user is supplied to addTask then just change your verify call to: verify(taskActionsMock).addTask(taskName,null)
However, that feels like a sidestep so instead you should ensure that connectedUser is not null and is the value you supplied to the verify call in your test case.
Your verify method specifies that you expect addTask to be called with specific taskName and user objects.
verify(taskActionsMock).addTask(taskName,user);
But since your connected user is null this expectation fails.
If you do not care about the connected user you can use matchers to tell Mockito to ignore its actual value. E.G.
verify(taskActionsMock).addTask(ArgumentMatchers.eq(taskName), ArgumentMatchers.any());
Or if you do care about the user just setup your ToDoEngine to have connected user.
Related
I'm trying to write unit test for my method called getBestSellers().
Here it is:
package bookstore.scraper.book.scrapingtypeservice;
import bookstore.scraper.enums.Bookstore;
import bookstore.scraper.book.Book;
import bookstore.scraper.fetcher.empik.EmpikFetchingBookService;
import bookstore.scraper.fetcher.merlin.MerlinFetchingBookService;
import bookstore.scraper.urlproperties.EmpikUrlProperties;
import bookstore.scraper.urlproperties.MerlinUrlProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import static bookstore.scraper.utilities.JSoupConnector.connect;
#Service
public class BestSellersService {
private final EmpikUrlProperties empikUrlProperties;
private final MerlinUrlProperties merlinUrlProperties;
private final EmpikFetchingBookService empikBookService;
private final MerlinFetchingBookService merlinBookService;
#Autowired
public BestSellersService(EmpikFetchingBookService empikBookService, MerlinFetchingBookService merlinBookService, EmpikUrlProperties empikUrlProperties, MerlinUrlProperties merlinUrlProperties) {
this.empikBookService = empikBookService;
this.merlinBookService = merlinBookService;
this.empikUrlProperties = empikUrlProperties;
this.merlinUrlProperties = merlinUrlProperties;
}
public Map<Bookstore, List<Book>> getBestSellers() {
Map<Bookstore, List<Book>> bookstoreWithBestSellers = new EnumMap<>(Bookstore.class);
bookstoreWithBestSellers.put(Bookstore.EMPIK, empikBookService
.get5BestSellersEmpik(connect(empikUrlProperties.getEmpik().getBestSellers())));
bookstoreWithBestSellers.put(Bookstore.MERLIN, merlinBookService
.get5BestSellersMerlin(connect(merlinUrlProperties.getMerlin().getBestSellers())));
return bookstoreWithBestSellers;
}
}
So, first I prepared test which looks like this:
package bookstore.scraper.book.scrapingtypeservice;
import bookstore.scraper.book.Book;
import bookstore.scraper.dataprovider.EmpikBookProvider;
import bookstore.scraper.dataprovider.MerlinBookProvider;
import bookstore.scraper.enums.Bookstore;
import bookstore.scraper.fetcher.empik.EmpikFetchingBookService;
import bookstore.scraper.fetcher.merlin.MerlinFetchingBookService;
import bookstore.scraper.urlproperties.EmpikUrlProperties;
import bookstore.scraper.urlproperties.MerlinUrlProperties;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.List;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class BestSellersServiceTest {
#Mock
private EmpikFetchingBookService empikBookService;
#Mock
private MerlinFetchingBookService merlinBookService;
#Mock
private EmpikUrlProperties empikUrlProperties;
#Mock
private MerlinUrlProperties merlinUrlProperties;
#InjectMocks
private BestSellersService bestSellersService;
#Test
public void getBestSellers() {
List<Book> merlinBestsellers = EmpikBookProvider.prepare5Bestsellers();
List<Book> empikBestsellers = MerlinBookProvider.prepare5Bestsellers();
when(empikBookService.get5BestSellersEmpik(any())).thenReturn(empikBestsellers);
when(merlinBookService.get5BestSellersMerlin(any())).thenReturn(merlinBestsellers);
//when(empikUrlProperties.getEmpik().getBestSellers()).thenReturn(anyString());
//when(merlinUrlProperties.getMerlin().getBestSellers()).thenReturn(anyString());
Map<Bookstore, List<Book>> actualMap = bestSellersService.getBestSellers();
Map<Bookstore, List<Book>> expectedMap = null;
assertEquals(expectedMap, actualMap);
assertThat(actualMap).hasSize(expectedMap.size());
}
}
Without setting behaviour for properties classes as I thought it is unnecessary, because I put any() when calling empikBookService.get5BestSellersEmpik (same for merlinBookService) but it threw NPE when calling
bookstoreWithBestSellers.put(Bookstore.EMPIK, empikBookService
.get5BestSellersEmpik(connect(empikUrlProperties.getEmpik().getBestSellers())));
I've debugged it and I have seen that
empikUrlProperties.getEmpik().getBestSellers()))
was given me NPE.
So I set behaviour like this:
when(empikUrlProperties.getEmpik().getBestSellers()).thenReturn(anyString());
when(merlinUrlProperties.getMerlin().getBestSellers()).thenReturn(anyString());
and now it is giving me here NPE with stactrace:
ava.lang.NullPointerException
at bookstore.scraper.book.scrapingtypeservice.BestSellersServiceTest.getBestSellers(BestSellersServiceTest.java:48)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
.
.
.
connect method used in tested method:
#UtilityClass
public class JSoupConnector {
public static Document connect(String url) {
try {
return Jsoup.connect(url).get();
} catch (IOException e) {
throw new IllegalArgumentException("Cannot connect to" + url);
}
}
}
Properties class (same for merlin)
package bookstore.scraper.urlproperties;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
#Getter
#Setter
#Component
#ConfigurationProperties("external.library.url")
public class EmpikUrlProperties {
private Empik empik = new Empik();
#Getter
#Setter
public static class Empik {
private String mostPreciseBook;
private String bestSellers;
private String concreteBook;
private String romances;
private String biographies;
private String crime;
private String guides;
private String fantasy;
}
}
What am I making wrong? Why it didnt work in the first place when I put any()
There are many problems, but the main one is that you're misunderstanding how mocking, argument evaluation and any() work.
You're using
when(empikBookService.get5BestSellersEmpik(any())).thenReturn(empikBestsellers);
This tells the mock empikBookService than whenever its get5BestSellersEmpik method is called, it should return empikBestsellers, whetever the argument passed to the method is.
What does your actual code pass as argument when executing your test? It passes the value returned by
connect(empikUrlProperties.getEmpik().getBestSellers())
The key part is that this expression is evaluated first, and then its result is passed as argument to the get5BestSellersEmpik() method.
Just like when you do
System.out.println(a + b)
a + b is first evaluated. If the result is 42, then the value 42 is passed to println(), and println prints 42.
So, in order for your test not to fail, the expression
connect(empikUrlProperties.getEmpik().getBestSellers())
must be evaluated successfully. Its result doesn't matter, since you've configure your mock to accept any argument. But that's irrelevant.
You're trying to do
when(empikUrlProperties.getEmpik().getBestSellers()).thenReturn(anyString());
That doesn't make any sense.
First, because empikUrlProperties.getEmpik() will return null, since empikUrlProperties is a mock, and mocks return null by default. And null.getBestSellers() will thus cause a NullPointerException.
Second, because telling a mock that it should return any string doesn't make sense. If you don't care about the string it should return, then choose a string by yourself, and make it return that. anyString() actually returns null, so you're telling it to return null.
So you need to fix that. Always think about what your code is doing instead of trying to apply a recipe.
And finally, your test also calls connect(...), which is a staic method, that you haven't (and can't) mock. This method will be called too. And it tries to connect to an actual URL. So if nothing is responding during your test at that URL, that won't work either. This connect() method should really be part of a dependency of your service, and this dependency should be mocked.
When you set your mock :
when(empikUrlProperties.getEmpik().getBestSellers()).thenReturn(anyString());
You have mocked empikUrlProperties, which is great, BUT you have not told that mock what to do when getEmpik() is called on it. Consequently, that method call will return null both here in the test, and also (before you had this line) in the production code - so this is the cause of your NPE when getBestSellers() is called.
Consequently, set that mock up, something like :
#Mock
private EmpikUrlProperties empikUrlProperties;
#Mock
private EmpikUrlProperties.Empik empikMock;
when(empikUrlProperties.getEmpik()).thenReturn(empikMock);
when(empikMock.getBestSellers()).thenReturn(anyString());
I'm new to all the mentioned technologies so it might be a stupid question.
We have a spring boot application where we need to write to a PostgreSQL-Database via JDBC.
Therefore we need the static DriverManager.getConnection() method to open the connection.
Now in my unit tests I don't want to call this class directly.
Instead I want to check, that the DriverManager.getConnection() is called with the correct String as that is my expected observable external behavior.
I encapsulated this behavior into a ConnectionFactory with the method newConnection(ConnectionType.POSTGRESQL) because we got more than one Database to use in this Application.
Now I can't find a way to verify via Mockito that this external dependency was called with the correct String like you could with an instance:
DriverManager dm = mock(DriverManager);
connectionFactory.newConnection(ConnectionType.POSTGRESQL);
verify(dm).getConnection("theConnectionStringToBeExpected");
So how to do this with the static dependency?
I tried the Captor-way but this seems to only work for direct usage like
mockStatic(DriverManager.class);
final ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
// What I have to do to verify
DriverManager.getConnection("theActualConnectionString");
// What I would like to do to verify
// connectionFactory.newConnection(ConnectionType.POSTGRESQL);
verifyStatic();
StaticService.getConnection(captor.capture());
assertEquals("theExpectedConnectionString", captor.getValue());
Edit:
Here is the nasty little workaround which I currently use for another server...
public void driverManagerIsCorrectlyCalledForAds() throws Exception {
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).load();
final Connection expectedConnection = mock(Connection.class);
when(DriverManager.getConnection("jdbc:extendedsystems:advantage://server:1337/database_name;user=user;password=password;chartype=ansi"))
.thenReturn(expectedConnection);
Connection actualConnection = connectionFactory.newConnection(ConnectionType.ADS);
assertEquals(expectedConnection, actualConnection);
}
Edit 2:
TestClass:
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.test.context.junit4.SpringRunner;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.powermock.api.mockito.PowerMockito.*;
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringRunner.class)
#PrepareForTest({ConnectionFactory.class, DriverManager.class, DatabaseDriverInformation.class})
public class ConnectionFactoryTest {
#InjectMocks
ConnectionFactory connectionFactory;
#Mock
DatabaseDriverInformation databaseDriverInformation;
#Mock
DatabaseProperties databaseProperties;
#Mock
DatabaseProperties.Pg pg;
#Mock
DatabaseDriverLoader databaseDriverLoader;
#Before
public void setUp() {
doReturn(pg).when(databaseProperties).getPg();
doReturn("server").when(ads).getServer();
doReturn(1338).when(ads).getPort();
doReturn("database_name").when(ads).getDatabasename();
doReturn("user").when(ads).getUser();
doReturn("password").when(ads).getPassword();
}
#Test
public void driverManagerIsCorrectlyCalledForPg() throws Exception {
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).load();
Connection expectedConnection = mock(Connection.class);
when(DriverManager.getConnection("jdbc:postgresql://server:1338/database_name;user=user;password=password"))
.thenReturn(expectedConnection);
Connection actualConnection = connectionFactory.newConnection(ConnectionType.POSTGRESQL);
assertEquals(expectedConnection, actualConnection);
}
}
Class under Test:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.*;
#Service()
public class ConnectionFactory {
#Autowired
private DatabaseDriverLoader databaseDriverLoader;
#Autowired
DatabaseProperties databaseProperties;
public Connection newConnection(ConnectionType connectionType) {
databaseDriverLoader.load();
final String connectionString = connectionStringFor(connectionType);
try {
return DriverManager.getConnection(connectionString);
} catch (SQLException sqlException) {
throw new RuntimeException("Couldn't connect to Server");
}
}
private String connectionStringFor(ConnectionType connectionType) {
switch (connectionType) {
case ADS:
return this.adsConnectionString();
case POSTGRESQL:
return this.pgConnectionString();
default:
throw new RuntimeException("Invalid connection Type requested!");
}
}
private String adsConnectionString() {
return new StringBuilder()
.append("jdbc:extendedsystems:advantage://")
.append(databaseProperties.getAds().getServer())
.append(":")
.append(databaseProperties.getAds().getPort())
.append("/")
.append(databaseProperties.getAds().getDatabasename())
.append(";user=")
.append(databaseProperties.getAds().getUser())
.append(";password=")
.append(databaseProperties.getAds().getPassword())
.append(";chartype=ansi")
.toString();
}
private String pgConnectionString() {
return new StringBuilder()
.append("jdbc:postgresql://")
.append(databaseProperties.getPg().getServer())
.append(":")
.append(databaseProperties.getPg().getPort())
.append("/")
.append(databaseProperties.getPg().getDatabasename())
.append("?user=")
.append(databaseProperties.getPg().getUser())
.append("&password=")
.append(databaseProperties.getPg().getPassword())
.toString();
}
}
I removed the package-names, some specific imports and some unnecessary tests which are working.
After some search I found this: How to verify static void method has been called with power mockito
In Essence:
first:
mockStatic(ClassWithStaticFunctionToVerify.class)
second:
Execute the Code that will call the function you want to verify later
third:
verifyStatic();
ClassWithStaticFunctionToVerify.functionYouWantToVerify("ParameterValueYouExpect");
With it's help I got the following Solution which works fine:
#Test
public void driverManagerIsCorrectlyCalledForPg() throws Exception {
// Arrange
mockStatic(DatabaseProperties.Pg.class);
doReturn(pg).when(databaseProperties).getPg();
doReturn("server").when(pg).getServer();
doReturn(1338).when(pg).getPort();
doReturn("database_name").when(pg).getDatabasename();
doReturn("user").when(pg).getUser();
doReturn("password").when(pg).getPassword();
mockStatic(DriverManager.class);
doNothing().when(databaseDriverLoader).loadAdsDriverClass();
doNothing().when(databaseDriverLoader).loadPgDriverClass();
when(DriverManager.getConnection(anyString())).thenReturn(expectedConnection);
// Act
connectionFactory.newConnection(ConnectionType.POSTGRESQL);
// Assert
verifyStatic();
DriverManager.getConnection("jdbc:postgresql://server:1338/database_name?user=user&password=password");
}
This question already has answers here:
How do I unit test code which uses Java UUID?
(4 answers)
Closed 5 years ago.
I'm trying to test a Web Service method which connects to a SQL Server Database which contains JCR nodes, as we're using JackRabbit.
The method looks like:
public String addDocumentByJson(String fileName, byte[] fileContent, int status, String userName, String jsonProperties) {
UUID id = UUID.randomUUID();
// It does a bunch of operations here
return jsonResult;
}
Where jsonResult is an object similar to this one:
{
"id" : "<A random UUID>"
"version" : 1
}
Now, when I try to test it following the steps in this answer and the code in this post and I came off with the following code (which as I said is based on the past links):
#PrepareForTest({ UUID.class })
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/TestSpringConfig.xml")
public class TestJackRabbitService {
#Autowired
#Qualifier("jackRabbitService")
IJackRabbitService jackRabbitService;
private byte[] fileContent;
private int versionFile;
public TestJackRabbitService() {
classLoader = getClass().getClassLoader();
}
#BeforeClass
public static void init() {
LOG.trace("Run #BeforeClass");
try {
fileContent = IOUtils.toByteArray(new FileInputStream(new File(Thread.currentThread().getContextClassLoader().getResource("fileTest.txt"))));
} catch (Exception e) {
LOG.error(ExceptionUtils.getStackTrace(e));
}
}
#Before
public void before() {
LOG.trace("Run #Before");
try {
versionFile = jackRabbitService.getLastVersionOf(nameApp, nameFile); //This method returns an int,
} catch (Exception e) {
LOG.error(ExceptionUtils.getStackTrace(e));
}
}
#Test
public void testAddDocumentsByJson() {
//Some tests which run correctly
final UUID uuid = UUID.randomUUID();
mockStatic(UUID.class);
LOG.debug(uuid);
//doReturn(uuid).when(UUID.randomUUID());
when(UUID.randomUUID()).thenReturn(uuid);
idFile = uuid;
assertEquals(jackRabbitService.addDocumentByJson(nameFile, bytes, nameApp, 5, jsonproperties), "{\"id\":\"" + uuid + "\",\"version\":1}");
}
}
However when I test this method it gives me the following results:
Results :
Failed tests:
testAddDocumentsByJson(com.optimissa.test.junit.TestJackRabbitService): expected:<{"id":"[1efaf3b8-ca7c-4e6f-878f-102d9a7a92d9]","version":1}> but was:<{"id":"[cfa1a8b0-be6a-46b1-90f5-d2f6d230796a]","version":1}>
As you can see both UUIDs are different, and from what I read on my first link in this question is that it should return the same UUID everytime the static method UUID.randomUUID() is called (the one stored in the uuid variable inside the TestJackRabbitService class...
I also tried with doReturn method as explained in this answer but it produces the following stack trace:
testAddDocumentsByJson(com.optimissa.test.junit.TestJackRabbitService) Time elapsed: 5.279 sec <<< ERROR!
org.mockito.exceptions.misusing.UnfinishedStubbingException:
Unfinished stubbing detected here:
-> at com.optimissa.test.junit.TestJackRabbitService.testAddDocumentsByJson(TestJackRabbitService.java:143)
E.g. thenReturn() may be missing.
Examples of correct stubbing:
when(mock.isOk()).thenReturn(true);
when(mock.isOk()).thenThrow(exception);
doThrow(exception).when(mock).someVoidMethod();
Hints:
1. missing thenReturn()
2. you are trying to stub a final method, which is not supported
3: you are stubbing the behaviour of another mock inside before 'thenReturn' instruction if completed
at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:182)
at org.powermock.core.MockGateway.doMethodCall(MockGateway.java:164)
at org.powermock.core.MockGateway.methodCall(MockGateway.java:134)
at com.optimissa.test.junit.TestJackRabbitService.testAddDocumentsByJson(TestJackRabbitService.java:143)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner$2.call(DelegatingPowerMockRunner.java:149)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner$2.call(DelegatingPowerMockRunner.java:141)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.withContextClassLoader(DelegatingPowerMockRunner.java:132)
at org.powermock.modules.junit4.internal.impl.DelegatingPowerMockRunner.run(DelegatingPowerMockRunner.java:141)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:121)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:57)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)
From this answer I read (but I don't understand) that maybe I need to create a new object from the class I'm trying to test? I'm injecting the dependency at the very beginning of the test class, I'm really new to JUnit testing and english is not my native language, however I can comprehend most of thing but that answer is giving me a hard time understanding it (due to my lack of knowledge in JUnit testing).
How can I make my JUnit test to retrieve the same ID which is generated inside the method (or intercept the call to UUID.randomUUD() to return the value inside my JUnit test) ?
Edit
After trying #hammerfest's answer, with the following changes:
UUID uuid = PowerMockito.mock(UUID.class);
mockStatic(UUID.class);
when(UUID.randomUUID()).thenReturn(uuid);
String jsonToCompare = "{\"id\":\"" + uuid + "\",\"version\":1}";
String jsonFromJRS = jackRabbitService.addDocumentByJson(nameFile, bytes, nameApp, 5, jsonproperties);
assertEquals(jsonFromJRS, jsonToCompare);
I still get this result:
testAddDocumentsByJson(com.optimissa.test.junit.TestJackRabbitService): expected:<{"id":"[493410b3-dd0b-4b78-97bf-289f50f6e74f]","version":1}> but was:<{"id":"[00000000-0000-0000-0000-000000000000]","version":1}>
Common mistake with mocking system classes it's that they are added to #PrepareForTest, but unfortunately it's impossible to mock final Java System classes directly. But PowerMock provides workaround. PowerMock replaces calls to system classes by call to PowerMock class. A class that use final system class should be added to #PrepareForTest
I've added example how to mock UUID.
public class DocumentService {
public JsonDocument saveDocument(JsonDocument document){
UUID uuid = UUID.randomUUID();
document.setId(uuid.toString());
return document;
}
}
Test
#RunWith(PowerMockRunner.class)
#PrepareForTest(DocumentService.class)
public class DocumentServiceTest {
#Test
public void should_set_id() throws Exception {
final String id = "493410b3-dd0b-4b78-97bf-289f50f6e74f";
UUID uuid = UUID.fromString(id);
mockStatic(UUID.class);
when(UUID.randomUUID()).thenReturn(uuid);
DocumentService documentService = new DocumentService();
JsonDocument document = new JsonDocument();
documentService.saveDocument(document);
assertThat(document.getId())
.as("Id is set")
.isEqualTo(id);
}
}
You may find more in documentation.
I've reused and modified #hammerfest s example a bit which work on my machine.
The first case simply mocks the static invocation of the UUID class and asserts that the returned UUID of the SUT equals the mocked UUID:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.UUID;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
#PrepareForTest({ UUID.class })
#RunWith(PowerMockRunner.class)
public class StaticMockTest {
#Test
public void test() {
MyClass sut = new MyClass();
UUID uuidLocal = UUID.randomUUID();
mockStatic(UUID.class);
when(UUID.randomUUID()).thenReturn(uuidLocal);
assertThat(sut.getUUID(), is(equalTo(uuidLocal)));
}
private class MyClass {
public UUID getUUID() {
return UUID.randomUUID();
}
}
}
The second case invokes a method of a Spring managed bean which returns the mocked UUID:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.BootstrapWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.test.context.support.DefaultTestContextBootstrapper;
import java.util.UUID;
import javax.annotation.Resource;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
#PrepareForTest({ UUID.class })
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringRunner.class)
#BootstrapWith(DefaultTestContextBootstrapper.class)
#ContextConfiguration(classes = {StaticMockTest2.ContextConfig.class},
loader= AnnotationConfigContextLoader.class)
#PowerMockIgnore({"javax.management.*"})
public class StaticMockTest2 {
#Resource
private MyClass sut;
#Test
public void test() {
UUID uuidLocal = UUID.randomUUID();
mockStatic(UUID.class);
when(UUID.randomUUID()).thenReturn(uuidLocal);
assertThat(sut.getUUID(), is(equalTo(uuidLocal)));
}
private static class MyClass {
public UUID getUUID() {
return UUID.randomUUID();
}
}
#Configuration
public static class ContextConfig {
#Bean
public MyClass myClass() {
return new MyClass();
}
}
}
Both tests work on my machine though I'd suggest to refactor the UUID generation into a utility class which you instantiate and inject via Spring. Then you can simply replace the PowerMock stuff with ordinary Mockito mocking and avoid dealing with such problems:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import java.util.UUID;
import javax.annotation.Resource;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {MockTest3.ContextConfig.class},
loader= AnnotationConfigContextLoader.class)
public class MockTest3 {
#Resource
private Util mockUtil;
#Resource
private MyClass sut;
#Test
public void test() {
UUID uuidLocal = UUID.randomUUID();
when(mockUtil.generateUUID()).thenReturn(uuidLocal);
assertThat(sut.getUUID(), is(equalTo(uuidLocal)));
}
private static class MyClass {
private Util util;
public MyClass(Util util) {
this.util = util;
}
public UUID getUUID() {
return util.generateUUID();
}
}
private static class Util {
public UUID generateUUID() {
return UUID.randomUUID();
}
}
#Configuration
public static class ContextConfig {
#Bean
public Util mockUtil() {
return mock(Util.class);
}
#Bean
public MyClass myClass() {
return new MyClass(mockUtil());
}
}
}
If you don't want to rely on Spring for testing (to speed up things even further) you can inject the dependencies yourself either via constructor injection or via Whitebox.setInternalState(sut, "fieldName", mockObject); or Springs ReflectionUtils.setField(sut, "fieldName", mockObject);.
import org.junit.Test;
import org.mockito.internal.util.reflection.Whitebox;
import java.util.UUID;
import javax.annotation.Resource;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
public class MockTest4 {
#Test
public void test() {
Util mockUtil = mock(Util.class);
MyClass sut = new MyClass(mockUtil);
// MyClass sut = new MyClass();
// Whitebox.setInternalState(sut, "util", mockUtil);
UUID uuidLocal = UUID.randomUUID();
when(mockUtil.generateUUID()).thenReturn(uuidLocal);
assertThat(sut.getUUID(), is(equalTo(uuidLocal)));
}
private class MyClass {
#Resource
private Util util;
public MyClass() {}
public MyClass(Util util) {
this.util = util;
}
public UUID getUUID() {
return util.generateUUID();
}
}
private class Util {
public UUID generateUUID() {
return UUID.randomUUID();
}
}
}
The last test contains both options, constructor or field injection, you can play with.
Due to #hammerfest s comment I'm adding a further example here that showcases what to do if MyClass is externally defined. Note this example was basically taken from Github before I read the answer of #ArthurZagretdinov, who is probably the author of this test in first place (as pointed out by #hammerfest in the comments). First the standalone MyClass implementation:
import java.util.UUID;
public class MyClass {
public UUID getUUID() {
return UUID.randomUUID();
}
}
Next, the test that uses the external MyClass definition:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import java.util.UUID;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.when;
#PrepareForTest({ MyClass.class })
#RunWith(PowerMockRunner.class)
public class StaticMockTest3 {
#Test
public void test() {
MyClass sut = new MyClass();
final String id = "493410b3-dd0b-4b78-97bf-289f50f6e74f";
UUID uuid = UUID.fromString(id);
// UUID uuidLocal = UUID.randomUUID();
mockStatic(UUID.class);
when(UUID.randomUUID()).thenReturn(uuidLocal);
// when(UUID.randomUUID()).thenReturn(uuidLocal);
assertThat(sut.getUUID().toString(), is(equalTo(uuid.toString())));
}
}
If you comment out both comment-lines in the above cenario, you will figure out that the test will fail due to unequal UUIDs. This means that the preparation for MyClass does also respect using the declared UUID mock and thus can be used for mocking static classes.
For the following classes Texts ...
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.util.Collections;
import java.util.List;
import hrisey.Parcelable;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#Parcelable
public final class Texts implements android.os.Parcelable {
#NonNull List<Text> texts = Collections.emptyList();
public boolean hasTexts() {
return !texts.isEmpty() && textsHaveValues();
}
private boolean textsHaveValues() {
for (Text text : texts) {
if (TextUtils.isEmpty(text.getValue())) {
return false;
}
}
return true;
}
}
... and Text ...
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import hrisey.Parcelable;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#Parcelable
public final class Text implements android.os.Parcelable {
private String textKey;
private String value;
}
... I wrote this unit test:
#RunWith(JUnit4.class)
public class TextsTest {
private Texts texts;
#Before
public void setUp() {
texts = new Texts();
}
#Test
public void hasTextsWithSingleEmptyItem() throws Exception {
texts.setTexts(Collections.singletonList(new Text()));
assertThat(texts.hasTexts()).isFalse();
}
}
The test succeeds in Android Studio 2.1.3 but it fails when I run ./gradlew clean test on my machine (MacOS 10.11.6, El Capitain, Java 1.7.0_79). Here is the error output:
com.example.model.TextsTest > hasTextsWithSingleEmptyItem FAILED
org.junit.ComparisonFailure: expected:<[fals]e> but was:<[tru]e>
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(
NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(
DelegatingConstructorAccessorImpl.java:45)
at com.example.model.TextsTest.hasTextsWithSingleEmptyItem(TextsTest.java:31)
Related posts
creating instance of object using reflection , when the constructor takes an array of strings as argument
IllegalArgumentException with constructing class using reflection and array arguments
How do you mock TextUtils? The part TextUtils.isEmpty(text.getValue()) should always be false when using the default Android test stubs.
Be sure to use a suitable implementation or consider using a different set of string utilities you already might have available with some other dependencies.
Edit by JJD
You are right, thanks! I use the Unmock plugin. So I had to unmock the relevant package to expose TextUtils in the unit tests:
unMock {
keepStartingWith "android.text."
}
Given this target code:
...
sessionWrapper.execute(arenaCreateCql, arenaGuid, arenaName, displayName, authorName, createdOn);
...
And Mockito code to validate that line:
...
#Captor
private ArgumentCaptor<Date> createdOnCaptor;
...
#Test
public void testThat_Execute_CreatesNewArena() throws Exception {
...
inOrder.verify(mockSessionWrapper).execute(
eq(arenaCreateCql), eq(testArenaGuid), eq(testArenaName), eq(testArenaDisplayName), eq(testAuthorName), createdOnCaptor.capture());
...
assertNotNull(createdOnCaptor.getValue());
}
This works using Mockito 1.9.5. When upgrading 1.10.8, the verify passes, but the getValue() fails with this error:
org.mockito.exceptions.base.MockitoException:
No argument value was captured!
You might have forgotten to use argument.capture() in verify()...
...or you used capture() in stubbing but stubbed method was not called.
Be aware that it is recommended to use capture() only with verify()
Edit to add MCVE. The following code runs green with Mockito 1.9.5, red with Mockito 1.10.8.
MockitoExample.java:
package org.makeyourcase.example;
import java.util.Date;
public class MockitoExample {
private MockitoExampleExecutor executor;
public void execute(){
executor.execute("var1", new Date());
}
}
MockitoExampleExecutor.java:
package org.makeyourcase.example;
public class MockitoExampleExecutor {
public void execute(Object... bindVariables){
}
}
MockitoExample_UT:
package org.makeyourcase.example;
import java.util.Date;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
#RunWith(MockitoJUnitRunner.class)
public class MockitoExample_UT {
#Mock
private MockitoExampleExecutor mockitoExampleExecutor;
#Captor
private ArgumentCaptor<Date> dateCaptor;
#InjectMocks
private MockitoExample subject;
#Test
public void testThat_Execute_InvokesCalendar() throws Exception {
subject.execute();
verify(mockitoExampleExecutor).execute(eq("var1"), dateCaptor.capture());
assertNotNull(dateCaptor.getValue());
}
}
One other piece of info came to light as a result of creating the MCVE - the test works fine if the Date is the only element passed for bindVariables. That is, remove "var1" from target and test code, then the test runs fine under 1.9.5 and 1.10.8. Also, it doesn't matter that the captor is for a Date. The same issue occurs if the parameter is of another type, such as Integer.
Thanks, this is probably a bug, I've created the report on GH-188.
Not sure when it will be fixed though. Fixed in GH-211.