I want to mock the static method being invoked from the constructor of my class.
My class:
package com.javaeasily.demos.junit;
import java.util.ArrayList;
public class MyClass {
private int number;
private static final ArrayList<String> ACTIVE_SERVICES_POST_RECONFIGURE = new ArrayList<>();
// Only allow construction if number is greater than one
MyClass() {
ACTIVE_SERVICES_POST_RECONFIGURE.add("my-node-" + NodeUtils.getMyNode());
}
public void reconfigureNode() {
if (ACTIVE_SERVICES_POST_RECONFIGURE.isEmpty()) {
return;
}
}
}
Here NodeUtils.getMyNode() is the static method being invoked from the constructor of the class.
NodeUtils.java Class:
package com.javaeasily.demos.junit;
import org.apache.maven.surefire.shade.booter.org.apache.commons.lang3.StringUtils;
public class NodeUtils {
private static final String HOSTNAME_PREFIX = "my-node-";
public static String hostnameToNode(String hostname) {
if (!hostname.startsWith(HOSTNAME_PREFIX)) {
throw new IllegalArgumentException(hostname + " is not recognized hostname");
}
return StringUtils.removeStart(hostname, HOSTNAME_PREFIX);
}
public static String getHostname() {
return System.getenv("HOSTNAME");
}
public static String getMyNode() {
return hostnameToNode(getHostname());
}
}
MyClassTest.java
package com.javaeasily.demos.junit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class MyClassTest {
private MyClass myclass;
#BeforeEach
public void SetUp() {
myclass = new MyClass();
}
#Test
public void testReconfigureNode() {
myclass.reconfigureNode();
}
}
When I try & run the only test case I get the following error:
java.lang.NullPointerException
at com.javaeasily.demos.junit.NodeUtils.hostnameToNode(NodeUtils.java:8)
at com.javaeasily.demos.junit.NodeUtils.getMyNode(NodeUtils.java:19)
at com.javaeasily.demos.junit.MyClass.<init>(MyClass.java:12)
at com.javaeasily.demos.junit.MyClassTest.SetUp(MyClassTest.java:11)
I am not sure how do we mock the method to avoid this error?
Since I am new to Java I am not able to catch this. Any help here is appreciated.
So to answer your question how to mock a static method: mockito allows this since version 3.8.0. You can find a tutorial here at Baeldung
This allows generating a statically mocked Object for a concrete context, which you can create within a try block. For your case this would look like the following.
Fixed Unit Test
package com.javaeasily.demos.junit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
public class MyClassTest {
private MyClass myclass;
#BeforeEach
public void SetUp() {
try (MockedStatic<NodeUtils> nodeUtilsMockedStatic = Mockito.mockStatic(NodeUtils.class)) {
nodeUtilsMockedStatic.when(NodeUtils::getMyNode).thenReturn("foo");
myclass = new MyClass();
}
}
#Test
public void testReconfigureNode() {
myclass.reconfigureNode();
}
}
Mockito dependency
You need mockito with at least version 3.8.0 in your project.
With maven add:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.8.0</version>
<scope>test</scope>
</dependency>
With gradle add:
testImplementation group: 'org.mockito', name: 'mockito-inline', version: '3.8.0'
Related
Why do I get a NullPointerExeption for testManuscript when trying to run my test?
This is my Manuscript.java:
package org.lhoffjann;
public class Manuscript {
private String msID;
private String path;
public void setMSid(String msID){
this.msID = msID;
}
public String getMSid() {
return this.msID;
}
}
This is my ManuscriptTest.java:
package org.lhoffjann;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class ManuscriptTest {
private static Manuscript testManuscript;
#Before
public void setUp(){
testManuscript = new Manuscript();
}
#Test
public void testGetMSid() {
testManuscript.setMSid("1234");
assertTrue("1234" == testManuscript.getMSid());
}
}
You have imported #Test from JUnit 5, while you're using #Before from JUnit 4, that combination doesn't work. You need to choose which JUnit version you want to use, 4 or 5, and then consistently import classes from that JUnit version. I would recommend using JUnit 5, and removing all JUnit 4 dependencies from your classpath, or at least configure your IDE to not suggest those imports.
For this specific case, replace #Before (org.junit.Before) with #BeforeEach (org.junit.jupiter.api.BeforeEach).
In the example as shown, you don't even need this setUp method, as each test-execution gets its own instance of the test class. You can use:
private Manuscript testManuscript = new Manuscript();
That is, remove static, initialize the field directly, and remove the setUp method.
Even if you continue to use the setUp method, I recommend removing the static, so testManuscript is an instance field, like it is actually used.
You have mixed Junit4 with Junit5. You should use only one version.
Junit4 or
package org.lhoffjann;
import org.junit.Before;
import org.junit.Test;
import org.junit.Assert;
public class ManuscriptTest {
private static Manuscript testManuscript;
#Before
public void setUp(){
testManuscript = new Manuscript();
}
#Test
public void testGetMSid() {
testManuscript.setMSid("1234");
Assert.assertEquals("1234",testManuscript.getMSid());
}
or
Junit5
package org.lhoffjann;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
class ManuscriptTest {
private static Manuscript testManuscript;
#BeforeEach
public void setUp() {
testManuscript = new Manuscript();
}
#Test
void testGetMSid() {
testManuscript.setMSid("1234");
Assertions.assertEquals("1234", testManuscript.getMSid());
}
}
In our service, we are initializing a bean (say "A") and that internally constructing a CacheableService Object by using - new CacheableService(). And as I know spring's #Cacheable annotations won't work on class method if the class is initialized using "new" Keyword.
Then what is an alternative or a way to cache method response?
Scenario :
<bean class="com.package.src.A"/>
public class A {
Map<String, CacheableService> map;
public CacheableService2() {
map = new HashedMap();
map.put("a", new CacheableService());
}
}
import org.springframework.cache.annotation.Cacheable;
public class CacheableService {
#Cacheable(value = "entityCount", key = "#criteria.toString()")
public int someEntityCount(final String criteria) {
System.out.println("Inside function : " + criteria);
return 5;
}
}
Here is a minimum example which demonstrates caching using Spring Boot. The code for the examples below can be found here.
Go to https://start.spring.io/ and create a new Spring Boot project. Make sure to include "Spring cache abstraction" which results in this entry being added to your pom:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
Add the #EnableCaching annotation to your application:
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
#EnableCaching
#SpringBootApplication
public class CacheableApplication {
public static void main(String[] args) {
SpringApplication.run(CacheableApplication.class, args);
}
}
Your service:
package com.example;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
#Service
public class CacheableService {
#Cacheable(value = "entityCount")
public int someEntityCount(final String criteria) {
System.out.print(String.format("Inside function: %s", criteria));
return 5;
}
}
Class A:
package com.example;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class A {
private CacheableService cacheableService;
public A(#Autowired CacheableService cacheableService) {
this.cacheableService = cacheableService;
}
public int getEntityCount(String criteria) {
return cacheableService.someEntityCount(criteria);
}
}
And then here is a test that demonstrates that the caching is working. As you can see in the test a.getEntityCount("foo") is being called twice, but in standard out we only see "Inside function: foo" being printed once. Therefore we have verified that the second call resulted in the cache being used to produce the result.
package com.example;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
#SpringBootTest
class CacheableTest {
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
#Autowired
private A a;
#BeforeEach
public void init() {
System.setOut(new PrintStream(outContent));
}
#Test
public void testCaching() {
a.getEntityCount("foo");
a.getEntityCount("foo");
assertEquals("Inside function: foo", outContent.toString());
}
}
EDIT:
If you want to move the cache outside of the Spring lifecycle and manually manage it then I would recommend using Caffeine. Here is the same example but now without any Spring involved.
Your service:
package com.example.withoutspring;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import java.util.concurrent.TimeUnit;
public class CaffeineCachingService {
private LoadingCache<String, Integer> entityCountCache = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
.build(key -> someEntityCount(key));
public int cachedEntityCount(final String criteria) {
return entityCountCache.get(criteria);
}
private int someEntityCount(final String criteria) {
System.out.print(String.format("Inside function: %s", criteria));
return 5;
}
}
Class B:
package com.example.withoutspring;
public class B {
private CaffeineCachingService cacheableService;
public B() {
cacheableService = new CaffeineCachingService();
}
public int getEntityCount(String criteria) {
return cacheableService.cachedEntityCount(criteria);
}
}
And the same test but without Spring:
package com.example.withoutspring;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CaffeineCacheableTest {
private final ByteArrayOutputStream outContent = new ByteArrayOutputStream();
private B b = new B();
#BeforeEach
public void init() {
System.setOut(new PrintStream(outContent));
}
#Test
public void testCaching() {
b.getEntityCount("foo");
b.getEntityCount("foo");
assertEquals("Inside function: foo", outContent.toString());
}
}
Obviously you need to tune the cache to perform how you want it so probably evicting the cached values after 5 minutes is not what you want but if you visit the Caffeine Github page you will see a lot of detailed examples how to configure the cache to meet your use-case.
Hope this helps!
I'm getting the above error whilst running my unit test for a java class in an Android project (in Android Studio).
The class under test:
import android.content.Context;
import android.util.Log;
import **.CustomObject;
import java.util.concurrent.CountDownLatch;
import androidx.annotation.NonNull;
public class CustomClass {
private static final String string = "a";
private static CustomObject customObject = null;
private static CountDownLatch initializedLatch = new CountDownLatch(1);
#NonNull
public static CustomObject1 getCustomObject1() {
try {
initializedLatch.await();
assert customObject != null;
return customObject;
} catch (InterruptedException e) {
throw new RuntimeException(".");
}
}
public static void methodA(final Context context,
final String string1,
) throws exception {
initializedLatch.countDown();
}
public static void methodB(#NonNull final CustomObject customObjectInput) {
customObject = customObjectInput;
}
}
The test class:
import android.content.Context;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.concurrent.CountDownLatch;
import **.CustomObject;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
#RunWith(MockitoJUnitRunner.class)
public class CustomClassTest{
#Mock
static CustomObject customObject;
#Mock
static Context context;
#Mock
CountDownLatch mCountDownLatch;
#Mock
CountDownLatch mInitializedLatch;
#InjectMocks
CustomClass customClass;
#Before
public void setUp() {
customObject = Mockito.spy(CustomObject.class);
context = Mockito.spy(Context.class);
}
#Test
public void customClassTest() {
doNothing().when(mInitializedLatch).countDown();
CustomClass.methodB(customObject);
try {
CustomClass.methodA(context, "");
} catch (Exception e) {
e.printStackTrace();
}
verify(mInitializedLatch).countDown();
try {
doNothing().when(mInitializedLatch).await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Class.getCustomObject();
}
The specific message I'm getting when running customClassTest:
Wanted but not invoked:
mInitializedLatch.countDown();
-> at CustomClassTest.methodA(CustomClassTest.java:79)
Actually, there were zero interactions with this mock.
Wanted but not invoked:
mInitializedLatch.countDown();
-> at CustomClassTest.methodA(CustomClassTest.java:79)
Actually, there were zero interactions with this mock.
Running the debugger with break points at each of the relevant lines seems to suggest that the test runs fine (with all the variables being assigned correctly at the right points) until verify(mInitializedLatch).countDown();, when the message appears (and the code stops running).
Any help appreciated, thanks.
UPDATE #1:
Altered the code to remove static keyword:
import android.content.Context;
import android.util.Log;
import **.CustomObject;
import java.util.concurrent.CountDownLatch;
import androidx.annotation.NonNull;
public class CustomClass {
private final String string = "a";
private CustomObject customObject = null;
private CountDownLatch initializedLatch = new CountDownLatch(1);
#NonNull
public CustomObject1 getCustomObject1() {
try {
initializedLatch.await();
assert customObject != null;
return customObject;
} catch (InterruptedException e) {
throw new RuntimeException(".");
}
}
public void methodA(final Context context,
final String string1,
) throws exception {
initializedLatch.countDown();
}
public void methodB(#NonNull final CustomObject customObjectInput) {
customObject = customObjectInput;
}
}
import android.content.Context;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.concurrent.CountDownLatch;
import **.CustomObject;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
#RunWith(MockitoJUnitRunner.class)
public class CustomClassTest{
#Mock
CustomObject customObject;
#Mock
Context context;
#Mock
CountDownLatch mCountDownLatch;
#Mock
CountDownLatch mInitializedLatch;
#InjectMocks
CustomClass customClass;
#Before
public void setUp() {
customObject = Mockito.spy(CustomObject.class);
context = Mockito.spy(Context.class);
}
#Test
public void customClassTest() {
doNothing().when(mInitializedLatch).countDown();
customClass.methodB(customObject);
try {
customClass.methodA(context, "");
} catch (Exception e) {
e.printStackTrace();
}
verify(mInitializedLatch).countDown();
try {
doNothing().when(mInitializedLatch).await();
} catch (InterruptedException e) {
e.printStackTrace();
}
customClass.getCustomObject();
}
Error messages now read:
error: non-static method methodA(Context,String) cannot be referenced from a static context
error: non-static method getCustomObject1() cannot be referenced from a static context
The second error message is displayed six times. It seems the code isn't compiling.
In CustomClass the CountDownLatch is declared as static field and it's initialized. If you debug your class you can see Mockito is not mocking/proxing this field. All the code interaction to initializedLatch object are not intercepted by Mockito proxy, so when you set-up your test by doNothing().when(mInitializedLatch).countDown(), actually you're not setting the field into customClass.So when you use verify(mInitializedLatch).countDown(), you're actually saying to Mockito that you expect one interaction with this mock, but no interactions are made due the reason above.
You are getting no invocations because the actual call is not made with your mocked mInitializedLatch object.
While mocking any object, you need to tell the compiler to use this mocked object instead of the one indeed present in your source implementation.
This can be achieved by making the object you are looking to test as an instance variable and passing the mocked object in the constructor.
Then the calls will be made from your mocked object and mockito will be able to track those.
Example:
// Source Code
public class CustomerClass {
private final CountDownLatch initializedLatch
public CustomerClass(CountDownLatch initializedLatch) {
this.initializedLatch = initializedLatch;
}
}
Now, use this instance variable in your code instead of the static variable you defined.
In test code, create constructor of CustomerClass by passing the mocked initializedLatch object and then it will work like charm.
If you are looking to initialize the value of initializedLatch there only. You can do the same by keeping a default constructor alongside the constructor I have defined above.
This default constructor can call the parameterized constructor.
public CustomerClass() {
this(new CountDownLatch(1));
}
Edit:
You also need to change your source implementation.
import android.content.Context;
import android.util.Log;
import **.CustomObject;
import java.util.concurrent.CountDownLatch;
import androidx.annotation.NonNull;
public class CustomClass {
private static final String string = "a";
private CustomObject customObject;
private CountDownLatch initializedLatch;
public CustomClass() {
this(new CountDownLatch(1), null);
}
public CustomClass(CountDownLatch initializedLatch, CustomObject customObject) {
this.initializedLatch = initializedLatch;
this.customObject = customObject;
}
#NonNull
public CustomObject1 getCustomObject1() {
try {
initializedLatch.await();
assert customObject != null;
return customObject;
} catch (InterruptedException e) {
throw new RuntimeException(".");
}
}
public void methodA(final Context context final String string1) throws Exception {
initializedLatch.countDown();
}
public void methodB(#NonNull final CustomObject customObjectInput) {
customObject = customObjectInput;
}
}
Now, above source implementation will use initializedLatch and customObject provided in the constructor.
Test code
import android.content.Context;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.concurrent.CountDownLatch;
import **.CustomObject;
import org.junit.Test;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnitRunner;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
#RunWith(MockitoJUnitRunner.class)
public class CustomClassTest{
#Mock
CustomObject customObject;
#Mock
Context context;
#Mock
CountDownLatch mInitializedLatch;
#InjectMocks
CustomClass customClass;
#Before
public void setUp() {
customClass = new CustomClass(mInitializedLatch, customObject);
}
#Test
public void customClassTest() {
doNothing().when(mInitializedLatch).countDown();
customClass.methodB(customObject);
try {
customClass.methodA(context, "");
} catch (Exception e) {
e.printStackTrace();
}
verify(mInitializedLatch).countDown();
try {
doNothing().when(mInitializedLatch).await();
} catch (InterruptedException e) {
e.printStackTrace();
}
customClass.getCustomObject();
}
Regarding the error you are getting, I don't think that's because of
the call you are making from the tests.
I want to test the following example code:
public class Example {
...
public void doStuff() {
...
Lift lift = new Lift();
lift.call(5);
...
}
...
}
How can I 'intercept' lift.call(5)?
Generally I would use when(lift.call(anyInt()).thenReturn(...), but I have no reference to the Lift object.
You can't do it with mockito alone. The cleanest solution is to refactor your code so you can have access to it. However if that's not an option then "power mockito" is what you want. Grab "powermock-api-mockito"+"powermock-module-junit4" and then something like this will do the trick:
import static org.mockito.Mockito.verify;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(Example.class)
public class ExampleTest {
private Example testSubject;
#Mock
private Lift lift;
#Test
public void testDoStuff() throws Exception {
testSubject.doStuff();
verify(lift).call(5);
}
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.whenNew(Lift.class).withNoArguments().thenReturn(lift);
testSubject = new Example();
}
}
Can you modify the Example class? If yes, the simplest way would be to extract the Lift dependency and provide it via constructor. Like this:
public class Example {
private final Lift lift;
public Example(Lift lift) {
this.lift = lift;
}
public void doStuff() {
this.lift.call(5);
}
}
Then you can stub lift as you want since now you have access to the instance.
I have a final class as below
public class firstclass{
private String firstmethod(){
return new secondclass("params").somemethod();
}
}
public final class secondclass{
secondclass(String params){
//some code
}
public String somemethod(){
// some code
return somevariable";
}
}
I have to here test first class so I have mocked this as below
secondclass classMock = PowerMockito.mock(secondclass .class);
PowerMockito.whenNew(secondclass .class).withAnyArguments().thenReturn(classMock);
Mockito.doReturn("test").when(classMock).somemethod();
But it is not mocking as I expected can anyone help me?
The method firstclass.firstmethod() is private method. So try to test this method through public method in which it is getting called.
You can mock SecondClass and its final method using #RunWith(PowerMockRunner.class) and #PrepareForTest(SecondClass.class) annotations.
Please see below the working code:
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(SecondClass.class)
public class FirstClassTest{
#Before
public void init() {
}
#After
public void clear() {
}
#Test
public void testfirstmethod() throws Exception{
SecondClass classMock = PowerMockito.mock(SecondClass.class);
PowerMockito.whenNew(SecondClass.class).withAnyArguments().thenReturn(classMock);
Mockito.doReturn("test").when(classMock).somemethod();
new FirstClass().firstmethod();
}
}
Libraries used: