This question already has an answer here:
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}>
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:
The second case invokes a method of a Spring managed bean which returns the mocked UUID:
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:
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 SpringsReflectionUtils.setField(sut, "fieldName", mockObject);
.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 standaloneMyClass
implementation:Next, the test that uses the external
MyClass
definition: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.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.
Test
You may find more in documentation.