PowerMock.expectPrivate behavior - unit-testing

I am writing test cases using EasyMock and PowerMock, and I want to modify the behavior of a private method so I am using below
PowerMock.expectPrivate(myClass, "m2", "Hello").andReturn("Something");
Now when this expect private statement is executed, it is actually invoking the REAL method.
This real method invocation is causing an issue because the private method has a few variables which are not available during the test execution and that's the reason why I want to mock this private method!
Above is defeating the purpose of private method mocking!!! Is that correct?
Below is my example class
#RunWith(PowerMockRunner.class)
#PrepareForTest({ReplicatePrivateMock.class})
public class ReplicatePrivateMock {
#Test
public void testPrivate() throws Exception {
MyClass myClass = new MyClass();
PowerMock.expectPrivate(myClass, "m2", "Hello").andReturn("Something");
System.out.println(myClass.m1("Hello"));
}
private class MyClass {
private StringBuilder builder;
public String m1(String s) {
return m2(s);
}
private String m2(String s) {
return builder.append(s) + s;
}
}
}
In this, I am mocking the call to m2() method but that throws NPE because m2 is using builder variable which is null!
Please let me know how to ACTUALLY mock the private method. Thanks very much.

Related

Mockito method call stub not working - Mockito.doReturn(false).when(studentServiceImpl).myClass().isValidUser(ArgumentMatchers.anyInt());

adding test cases for getStudent method, this is having internal calls
1- is repository call - stubbing this call working fine
2- validate user call - stubbing this call not working, showing some error and test case failed.
Service Class
#Service
public class StudentServiceImpl implements StudentService {
#Autowired
FakeStudentRepository fakeStudentRepository;
#Override
public Optional<Student> getStudent(int id) {
Optional<Student> student = fakeStudentRepository.getStudent(id);
boolean isValid = myClass().isValidUser(student.get().getId());
if(!isValid) {
return Optional.empty();
}
return student;
}
public MyTestClass myClass() {
return new MyTestClass();
}
}
MyTestClass
public class MyTestClass {
public boolean isValidUser(int id) {
return true;
}
}
Test Class
#SpringBootTest
class StudentServiceImplTest {
#Mock
FakeStudentRepository fakeStudentRepository;
#InjectMocks
StudentServiceImpl studentServiceImpl;
#BeforeEach
public void setup() {
studentServiceImpl = Mockito.spy(StudentServiceImpl.class);
MockitoAnnotations.initMocks(this);
}
#Test
void getStudent() {
Optional<Student> student = Optional.of(Student.builder().id(1).firstName("Rahul").lastName("rahul")
.mobile("XXXXXX").build());
Mockito.doReturn(student)
.when(fakeStudentRepository).getStudent(ArgumentMatchers.anyInt());
Mockito.doReturn(false)
.when(studentServiceImpl).myClass().isValidUser(ArgumentMatchers.anyInt());
Optional<Student> resultStudent = studentServiceImpl.getStudent(student.get().getId());
assertEquals(resultStudent.get().getId(), student.get().getId());
}
}
Error
org.mockito.exceptions.misusing.WrongTypeOfReturnValue: Boolean
cannot be returned by myClass() myClass() should return MyTestClass
If you're unsure why you're getting above error read on. Due to the
nature of the syntax above problem might occur because:
1. This exception might occur in wrongly written multi-threaded tests. Please refer to Mockito FAQ on limitations of concurrency
testing.
2. A spy is stubbed using when(spy.foo()).then() syntax. It is safer to stub spies -
- with doReturn|Throw() family of methods. More in javadocs for Mockito.spy() method.
The error Message says it: You are mocking studentServiceImpl.myClass() and try to return true. It’s not possible to mock the end of a call chain as you try with your second Mockito expression.
To do what you want requires to mock myClass() first by returning a mocked class instance and mock isValidUser on that.

Unit testing principles

When I write unit tests should I create one or more test methods for each and every method that I create in the source file? If the method calls 3 other methods , should response of all those methods be mocked?
As a primer, I would say yes, you should add at least one test for each public method (with the experience you will find some methods are not worthy or if they already tested by other tests).
Regarding mocks, I would just mock the minimum in order to make the method under test work as expected. Too much mocking might smell of poor design.
Can those 3 other methods be called from other functions?
If yes then you'd need to test them out individually.
If no then likely those methods should be marked as private and just test your public interface ie the one which calls all three. If that test works then it means your private API is written correct.
But it's usually easier to break/fail a test at a smaller more focused function than a high level function.
I'm sure you will get many varying opinions on this. But in my experience, generally you would have at least 1 test for each public method. I start with happy path and then explore the edge cases and error scenarios. I mock out only external calls, private methods can be tested via the public methods, or if complex, I either extract them to a delegate that can be tested independently or I loosen the accessibility (i.e. protected in Java) so that the method can be tested directly (judgement call based on sensitivity/design considerations).
In Java / JUnit / Mockito:
public class SystemUnderTest {
private SomeDependency dependency;
public Response doSomething(String parameter) {
String foo = dependency.doSomethingForMe(parameter);
String bar = privateMethod(foo);
return new Response(bar);
}
private String privateMethod(String in) {
if("foo".equals(in)) return "bar";
else return "baz";
}
protected String complexNonPublicMethod(String a, String b, String c) {
.. does complicated things ..
}
}
public class SystemUnderTestTest { // Pardon the ugly name
#Mock
private SomeDependency dependencyMock;
#InjectMocks
private SystemUnderTest sut;
#Test
public void doSomething_happyPath() {
String input = "input";
String mockFoo = "foo";
when(dependencyMock.doSomethingForMe(input)).thenReturn(mockFoo);
String expected = new Response("bar");
String actual = sut.doSomething();
assertEquals(expected, actual); // JUnit syntax OR
assertThat(actual, is(expected)); // Hamcrest syntax (which I prefer)
verify(dependencyMock, times(1)).doSomethingForMe(input);
verifyNoMoreInteractions(dependencyMock);
}
#Test
public void doSomething_inputIsNull() {
String input = null;
String mockFoo = "foo";
when(dependencyMock.doSomethingForMe(input)).thenThrow(new NullPointerException("Input is required!"));
try {
sut.doSomething();
fail("Expected exception not thrown.");
} catch (Exception e) {
assertThat(e instanceof NullPointerException.class, is(true));
assertThat(e.getMessage, is("Input is required!"));
}
}
// Protected accessibility frees you from having to test this via some other public method
#Test
public void complexNonPublicMethod_happyPath() {
String expected = <whatever>;
String actual = sut.complexNonPublicMethod(a, b, c);
assertThat(actual, is(expected));
}
// Protected accessibility frees you from having to test this via some other public method
#Test
public void complexNonPublicMethod_NullInput_A() {
String expected = <whatever>;
String actual = sut.complexNonPublicMethod(null, b, c);
assertThat(actual, is(expected));
}
// Protected accessibility frees you from having to test this via some other public method
#Test
public void complexNonPublicMethod_NullInput_B() {
String expected = <whatever>;
String actual = sut.complexNonPublicMethod(a, null, c);
assertThat(actual, is(expected));
}
.. etc ..
}
Good Luck!

How to write unit tests for classes with CheckedProviders in their constructors

I have a class under test whose constructer looks like this :
public class ClassUnderTest {
ClientOne clientOne;
ClientTwo clientTwo;
OtherDependency otherDependency;
#Inject
public ClassUnderTest(MyCheckedProvider<ClientOne> myCheckedProviderOne,
MyCheckedProvider<ClientTwo> myCheckedProviderTwo,
OtherDependency otherDependency) throws Exception {
this.clientOne = myCheckedProviderOne.get();
this.clientTwo = myCheckedProviderTwo.get();
this.otherDependency = otherDependency;
}
.
.
.
}
And the CheckedProvider looks thus :
public interface MyCheckedProvider<T> extends CheckedProvider<T> {
#Override
T get() throws Exception;
}
I could mock the clients, but how do I initialise the providers with my mocked clients.I use a combination of junit and mockito for writing tests.Any inputs would be appreciated.
What you could do is to mock providers rather than clients. ClientOne and ClientTwo are the types you are passing into your generic class, they are not variables and hence not something you want to mock. In contrast, the providers you are passing to the constructor are really variables, and what you need to control (simulate) are the behaviors of these variables.
public class ClassTest {
private static final CientOne CLIENT_ONE = new ClientOne();
private static final ClientTwo CLIENT_TWO = new ClientTwo();
#Mock
private MyCheckedProvider<ClientOne> providerOne;
#Mock
private MycheckedProvider<ClientTwo> providerTwo;
private ClassUnderTest classUnderTest;
#Before
public void setUp() {
when(providerOne.get()).thenReturn(CLIENT_ONE);
when(providerTwo.get()).thenReturn(CLIENT_TWO);
classUnderTest = new ClassUnderTest(providerOne, providerTwo, otherDependency);
}
}
As the other answer suggests, you could easily mock the providers, too.
But youMyCheckedProvider don't have to.
You already have an interface sitting there, so what would prevent you from creating something like
class MyCheckedProviderImpl<T> implements MyCheckedProvider<T> {
and that think takes a T object in its constructor and returns exactly that?
That is more or less the same what the mocking framework would be doing.

Mockito: stub function is not working

I am using Mockito to write a simple unit test.
Then, a function under test:
public class MyService {
public void getData() {
executor.execute(new MyRunnable() {
#Override
doTask() {
MyRestClient client = getRestClient();
Response resp = client.getFromServer();
persist(resp.getData());
}
});
}
}
protected MyRestClient getRestClient() {
return new MyRestClient();
}
My test case, I want to test doTask() has run & resp.getData() is persisted:
#Test
public void testGetData() {
MyService spyService = spy(MyService.getInstance());
// mock client
MyRestClient mockedClient = mock(MyRestClient.class);
mockedClient.setData("testData");
// stub getRestClient() function to return mocked client
when(spyService.getRestClient()).thenReturn(mockedClient);
// SUT
spyService.getData();
// run the Runnable task.
Mockito.doAnswer(new Answer<Object>() {
public Object answer(InvocationOnMock invocation) throws Exception {
Object[] args = invocation.getArguments();
Runnable runnable = (Runnable) args[0];
runnable.doTask();
return null;
}
}).when(executor).execute(Mockito.any(Runnable.class));
...
}
As you see above, I stub the getRestClient() function to return a mocked MyRestClient. However when run the test case, it doesn't stub the getRestClient() but run the real function. Why?
[Edit] following comment and review feedback
A rule of thumb is not to mock the class under test. Also your testing will be much easier if your class under test does not use the new keyword. Instead use Factory classes to create objects. There will be no need to use Mockito.spy() only Mockito.mock().
The fact that the following answer requires significant test setup is telling you that MyService has too much reposibility and needs to be simplified. However for the sake of answering your question directly here is how you can refactor your code to support verifying the call to persist() using Mocks.
MyService accepts in the constructor the objects that you will be mocking in your test setup. Having them passed into the constructor allows your JUnit test case to create the Mocks and keep a reference to them for verification later.
public class MyService {
private MyRunnableFactory runFactory;
private MyRestClientFactory restFactory;
private MyRestDao dao;
// inject constructor arguments
public MyService(MyRunnableFactory runFactory, MyRestClientFactory restFactory, MyRestDao dao) {
this.runFactory = runFactory;
this.restFactory = restFactory;
this.dao = dao;
}
public void getData() {
MyRestClient restClient = restFactory.createInstance();
MyRunnable runner = runFactory.createInstance(restClient, dao);
executor.execute(runner);
}
}
MyRunnable is created so that it can be tested in isolation if required. Again we inject the Mock objects into the constructor. It is tempting to inline Runnables as you have written in your question, however you lose the ability to control the new instance being created within you tests.
public class MyRunnable implements Runnable {
private MyRestClient restClient;
private MyRestDao dao;
public MyRunnable(MyRestClient restClient, MyRestDao dao) {
this.restClient = restClient;
this.dao = dao;
}
public void run() {
Response resp = restClient.getFromServer();
dao.persist(resp.getData());
}
}
MyRestDao is created because this is the class that you want to Verify in your test case. I don't see where persist() is defined in your question so we create a Data Access Object (DAO) to implement it.
public class MyRestDao {
public void persist() {
// save to some repository
}
}
Now let's write the test case that uses the above classes. We want to verify that the persist() method has been called
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#Mock MyRestDao dao;
#Mock MyRestClient restClient;
#Mock MyRunnableFactory runFactory;
#Mock MyRestClientFactory restFactory;
#Test
public void testPersistIsCalled() {
Response expectedResponse = new Response("some data"); // real implementation, not mocked
MyRunnable runner = new MyRunnable(restClient, dao); // real implementation, not mocked
when(restFactory.createInstance()).thenReturn(restClient);
when(runFactory.createInstance(restClient, dao)).thenReturn(runner);
when(restClient.getFromServer()).thenReturn(expectedResponse);
when(restClient.getData()).thenReturn(myRunnable);
// method under test
MyService service = new MyService(runFactory, restFactory);
service.getData();
verify(dao).persist(expectedResponse.getData());
}
}
Note that this test case is brittle because it is tightly coupled to the actual implementation of the MyService class. Ideally you want tests that don't need to know about the internal workings of your class under test.

Unit test for EJB with #PostConstruct method

Consider the following sample code:
#Stateless
public class MyBean {
private SomeHelper helper;
private long someField;
#PostConstruct
void init() {
helper = new SomeHelper();
someField = initSomeField();
}
long initSomeField() {
// perform initialization
}
public void methodToTest() {
helper.someMethod();
long tmp = 3 + someField;
}
}
And here is the test template, that I always use
public class MyBeanTest {
#Spy
#InjectMocks
private MyBean testSubject;
#Mock
private SomeHelper mockedHelper;
#Before
public void before() {
MockitoAnnotations.initMocks(this);
doReturn(1L).when(testSubject).initSomeField();
}
#Test
public void test() {
testSubject.methodToTest();
// assertions
}
}
The problem with testing methodToTest is that it needs field someField to be initialized. But the initialization is done in #PostConstruct method. And I can't run this method before call to testSubject.methodToTest(), because it will re-initialize helper. Also, I don't want to manually set up all the mocks. And I don't want to use reflection to set the someField, because that would make MyBeanTest vulnerable to MyBean refactoring. Can anybody propose, maybe better design to avoid situations like this?
A few notes:
Logic in initSomeField could be quite heavy (including calls to database), so I want to initialize it only once in a #PostConstruct method.
I don't want to create a setter for this field or widen its access modifier, because that would allow unwanted changes to my field.
If your test is in the same package as your class, then you can just call initSomeField directly, since it's package private. You can either do this in each individual test method, or in your #Before method, provided it runs after initMocks.