I've been looking at forums all day trying to mock a class, but none of the solutions have worked for me, and I have no clue what I'm doing wrong (I'm new to Java testing).
I have this (renamed) class that I want to test, mainly each if condition to ensure they work:
public class MyClass {
public static List<Object> MyClass(#NotNull Session session, #NotNull MyObject updated, #NotNull Mode mode)
throws Exception {
ModeList modes = MyUtil.geModes(mode);
try {
final Transaction tr = updated.getTransaction();
MyObject original;
if(tr == null) {
original = new Foo().getCurrentBar(session, updated.getKey(), null, "en");
} else {
original = new Foo().getOtherBar(session,
updated.getKey(),
tr.getProp1().intValue(),
tr.getProp2().intValue(),
"en");
}
return SomeClass.check(updated, original, modes);
} catch (Exception ex) {
// handle
}
return null;
}
}
Please note I can't change this (or the Foo class) / add dependency injection, it needs to be tested as is. I have tried mockito, powerMock mock, mockStatic, partial mocks, etc, they all fail for one reason or another, and the real getCurrentBar / getOtherBar get called.
I may be getting tunnel vision at this point, but please advise.
This code is wrong, it should use a factory to get Foo, so you can mock the factory. But since you can't modify it then the next best thing is to use PowerMock.
You can follow this guide: https://github.com/powermock/powermock/wiki/MockConstructor make sure you do not forget the #RunWith and #PrepareForTest annotations.
Related
Given synchronized and Lombok's #Synchronized, the latter causes a NullPointerException when mocking a method under test. Given
public class Problem
{
public Problem()
{
// Expensive initialization,
// so use Mock, not Spy
}
public synchronized String a()
{
return "a";
}
#Synchronized // <-- Causes NPE during tests, literally, here
public String b()
{
return "b";
}
}
and the Jupiter test class
class ProblemTest
{
#Mock
private Problem subject;
#BeforeEach
void setup()
{
initMocks(this);
// There is more mocking. Please don't let the simplicity
// of this example throw you off.
doCallRealMethod().when( subject ).a();
doCallRealMethod().when( subject ).b();
// This is a hack, but works. Can we rely on this?
// ReflectionTestUtils.setField( subject, "$lock", new Object[0] );
}
#Test
void a()
{
// Succeeds
assertEquals( "a", subject.a() );
}
#Test
void b()
{
// NullPointerException during tests
assertEquals( "b", subject.b() );
}
}
Lombok adds something like the following:
private final Object $lock = new Object[0]; // We can't rely on this name
...
public String b()
{
synchronized($lock)
{
return "b";
}
}
How to mock a method that is decorated with Lombok's default #Synchronized annotation?
Here is the stack trace, though it is unhelpful. I suspect Lombok adds a field as in my example above, and of course that is not injected into the mock, so voilĂ , NPE.
java.lang.NullPointerException
at com.ericdraken.Problem.b(Problem.java:16) // <-- #Synchronized keyword
at com.ericdraken.ProblemTest.b(ProblemTest.java:43) // <-- assertEquals( "b", subject.b() );
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
... [snip] ...
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
This isn't an issue with Lombok, the following also fails.
#ExtendWith({MockitoExtension.class})
#MockitoSettings(strictness = Strictness.LENIENT)
public class ProblemTest {
#Mock
private Problem subject;
#BeforeEach
void setup()
{
doCallRealMethod().when( subject ).c();
}
#Test
void c()
{
// NullPointerException during tests
assertEquals( "c", subject.c() );
}
}
class Problem
{
private final Map<String,String> c = new HashMap<>(){{put("c","c");}};
public String c(){
return c.get("c");
}
}
To be precise, you are not really mocking Problem, you are partially mocking via doCallRealMethod hence the issue.
This is also called out in Mockito's documentation,
Mockito.spy() is a recommended way of creating partial mocks. The reason is it guarantees real methods are called against correctly constructed object because you're responsible for constructing the object passed to spy() method.
doCallRealMethod() is called on a mock which is not guaranteed to have the object created the way it's supposed to be.
So to answer your question, yes that's the way you create a mock, but doCallRealMethod is always a gamble irrespective of Lombok.
You can use spy if you really want to call the actual method.
#Test
void c() {
Problem spyProblem = Mockito.spy(new Problem());
assertEquals("c", spyProblem.c());
verify(spyProblem, Mockito.times(1)).c();
}
The core problem is that you are combining calling the real method with a mock rather than a spy. This is dangerous in general, as whether it works for anything depends very much on the internal implementation of the method in question.
Lombok only matters because it works by altering that internal implementation during compilation, in a way that happens to require proper object initialization to work where the original method does not.
If you're going to configure a mock to call the real method, you should probably use a spy instead.
Synopsis
Project Lombok has the #Synchronized annotation on methods to hide the underlying and auto-generated private lock(s), whereas synchronized locks on this.
When using a Mockito mock (not a spy, because there are situations when we don't want a full object instantiated), fields are not initialized. That means as well the auto-generated "lock" field is null which causes the NPE.
Solution 1 - Field injection
Looking at Lombok source code, we see that Lombok uses the following lock names:
private static final String INSTANCE_LOCK_NAME = "$lock";
private static final String STATIC_LOCK_NAME = "$LOCK";
Unless Lombok suddenly changes this in the future, this means we can do field injection even if it feels like a "hack":
#BeforeEach
void setup()
{
initMocks(this);
...
ReflectionTestUtils.setField( subject, "$lock", new Object[0] );
}
Solution 2 - Declare a lock, then field injection
The question asks about #Synchronized, not #Synchronized("someLockName"), but if you can explicitly declare the lock name, then you can use solution one with confidence about the lock field name.
I want to have 100% coverage on the method toString overriden with Jackson JSON.
#Override
public String toString() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
logger.error(e.getMessage());
return "";
}
}
I can make a test that can coverage the most of the code except the catch block.
#Test
public void testToString() {
TestClass testClass = new TestClass();
String expected = "{\"testAttr\":null}";
assertEquals(expected, testClass.toString());
}
How could I make a test that covers the catch block?
Of course you cold try to trigger that exception somehow with setting the enclosing object to some weird kind of state. But a better way to achieve full code coverage is mocking the mapper and making it throw the desired exception. Generally the steps that you need are:
Transfer the creation of ObjectMapper to a new method 'getObjectMapper'. During runtime it will decide if it returns a real or a fake mapper.
In your test inject a fake ObjectMapper and make 'getObjectMapper' return the fake mapper instead of a new mapper.
When the writeValueAsString method is called make it throw a JsonProcessingException
Run your test
In theory you could manually create the fake ObjectMapper e.g. by creating a subclass of it but that's not the recommended way. I would recommend using a mocking framework. A mocking framework lets you express things like "when method A is called then throw exception B".
You can define some attributes to your class and assert the return of the string.
You've already tested the null object.
Raise the exception and see if it handles it.
Like:
when(mapper.writeValueAsString(any(Object.class))).thenThrow(new JsonProcessingException("Error"){});
And also helpful link
Let's say that I have a some service class
class Service<T> {
T get(int id);
void save();
}
In my unit tests I mock both methods of this service using mockito. But there is a case when method save should be mocked based on mocked get method. For example if method get is called with an argument which is equal to 2 then method save should throw some exception.
I went through Mockito documentation but seems like have not found any solutions yet.
Any ideas how I can achieve this use case with Mockito?
1) what you are trying to do is calling a test case from from a test case.
2) Mocking is used mainly for testing the endpoint, may be for controllers.So if your save functions needs to called again or you have to reuse the code for other test case, you should do that, it has no problem.
But what you are saying is totally against the programming paradigms for mock test cases.
In my opinion needing a mocking like that should be avoided, but if you insist you can leverage the doAnswer() method for detailed mock handling.
Basically, you use two Answer instances. One that sets a flag when the method is called with 2, the other one resetting the flag.
Another answer reacts to that flag and throws an exception if needed.
private boolean throwExceptionNext;
#Test
public void test() {
Service mock = Mockito.mock(Service.class);
Mockito.doAnswer((__) -> {
throwExceptionNext = false;
return new Object();
}).when(mock).get(Mockito.anyInt());
Mockito.doAnswer((__) -> {
throwExceptionNext = true;
return new Object();
}).when(mock).get(2);
Mockito.doAnswer((__) -> {
if (throwExceptionNext)
throw new RuntimeException();
return null;
}).when(mock).save();
mock.get(3);
mock.save();
mock.get(2);
try {
mock.save();
Assert.fail();
} catch (RuntimeException e) {
}
mock.get(3);
mock.save();
}
I need a clarification about mocking tests. In my particular scenario, I have to test a service, that has the dependency on the connecting module, the connector. Basically what connector does, is that it creates an instance of the service call that has to be made. I will demonstrate on the example.
public DataService(Connector connector) {
this.connector = connector;
}
#Override
public ServiceData getWeatherData(String dataId) throws ServiceCommunicatonException {
try {
return connector.newGetWeatherDataCall().call(
WeatherData.builder().withId(dataId).build());
} catch (Exception e) {
throw new ServiceCommunicatonException(ERR_MSG);
}
}
So the connector.newGetWeatherDataCall() returns the instance of the type WeatherData.
Now, in order to test the Service, I guess I would need to mock the Connector. Mocking the Service is probably pointless, because then I am not really testing it, right?
I tried mocking the Connector with something like this:
#Before
public void setUp() {
connector = mock(Connector.class);
}
#Test
public void getDataTest() {
assertNotNull(service.getData("123"));
}
However, this is obviously wrong, because this gave ma NullPointerException because WeatherDataCall from this line: return
connector.newGetWeatherDataCall().call(
WeatherData.builder().withId(dataId).build()); was null.
So how should this be properly tested/mocked? It would be great with some code examples.
#Test
public void getDataTest() {
WeatherData getWeatherDataResponse = Mockito.mock(WeatherData.class);
when(connector.newGetWeatherDataCall()).thenReturn(getWeatherDataResponse);
when(getWeatherDataResponse.call(Matchers.any(Class.class))).thenReturn(new ServiceData());
assertNotNull(service.getData("123"));
}
Explanation:-
You got null because you did not set the expected return value. Actually connector.newGetWeatherDataCall() returns null. This is because you did not use Mockito.when() to return your expected results.
Second : In your case, a method on this return value is invoked so connector.newGetWeatherDataCall() should return a mock of WeatherData. And now you will set an expectation for weatherData.call(..) which will be ServiceData type.
You can also set up the mock to throw an exception, which is useful if the catch clause does a bit more work.
public class First {
public First(){
}
public String doSecond(){
Second second = new Second();
return second.doJob();
}
}
class Second {
public String doJob(){
return "Do Something";
}
}
Here I want to test the method "doSecond()" of class "First". For the same, I want to mock the method "doJob" of class "Second".
I know that I can create a mocked instance of class "Second" using the code below.
Second sec = mock(Second.class);
when(sec.doJob()).thenReturn("Stubbed Second");
But I cannot relate this mocked instance with class "First" as of the current code.
Without refactoring the source code, is there any way by which i can achieve the requirement.
Please help.
Take a look at powermock's ability to intercept calls to new and return mocks instead
https://code.google.com/p/powermock/wiki/MockConstructor
This doesn't require changing any sourcecode.
here's the test code where we actually return a mock when First.doSecond() calls new Second()
#RunWith(PowerMockRunner.class)
#PrepareForTest(First.class)
public class TestFirst {
#Test
public void mockSecond() throws Exception{
Second mock = PowerMockito.mock(Second.class);
PowerMockito.whenNew(Second.class).withNoArguments().thenReturn(mock);
PowerMockito.when(mock.doSecond()).thenReturn("from mock");
First first = new First();
assertEquals("from mock", first.doSecond());
}
}
It's tricky to mock an instance that you create inside of a method, but it's possible.
Using PowerMock, you can accomplish this with the PowerMock.expectNew() method:
#RunWith(PowerMockRunner.class)
#PrepareForTest(First.class)
public class StackOverflowTest {
#Test
public void testFirst() throws Exception {
Second secondMock = EasyMock.createMock(Second.class);
PowerMock.expectNew(Second.class).andReturn(secondMock);
expect(secondMock.doSecond()).andReturn("Mocked!!!");
PowerMock.replay(secondMock, Second.class);
String actual = new First().doSecond();
PowerMock.verify(secondMock, Second.class);
assertThat(actual, equalTo("Mocked!!!"));
}
}
Effectively, PowerMock is proxying the creation of the new object and substituting whatever value we want when we invoke doSecond().
So, it's possible. However, this is a terrible practice to get into.
One typically wants to mock objects if they involve an outside concern, such as another layer (i.e. database, validation), or if the desired output is coming from other objects that are injected but are safe enough to consider tested.
If your method is capable of getting or retrieving data from a non-injectable source, you should not want to mock that out.
Considering that your method is simple and straightforward, you should really not need to do any mocks here at all. But if you felt that you were forced to, you could do one of a few things:
Create a factory for the creation of Second, and mock the results of the returning factory object with Mockito.
Pass in an instance of Second to that method, and use Mockito as the mock instance.
Declare it as a field (i.e. injected dependency), and use Mockito.
For completeness, here is how the test can be written with the JMockit mocking API, without any refactoring of the original code under test:
public class ExampleTest
{
#Test
public void firstShouldCallSecond(#Mocked final Second secondMock) {
new NonStrictExpectations() {{
secondMock.doJob(); result = "Mocked!!!";
}};
String actual = new First().doSecond();
assertEquals("Mocked!!!", actual);
}
}