Is it possible to do nested spying with Mockito? - java

Let's assume I have an object like this:
public ClassA() {
public void start() {
...
ClassB something = getSomeStuff();
...
}
protected ClassB getSomeStuff() {
return new ClassB(render, response);
}
}
Then I'm trying to do some unit-testing like this where I do a spy on a method-call on a spied object:
#Spy private ClassA classA;
#Mock private ClassC classC;
...
#Test
void test1() {
ClassB classB = spy(classA.getSomeStuff());
doReturn(classC).when(classB).getResults();
classA.start();
}
When I run this test and it comes down to this last line, I can see in my debugger that it is not returning classC.
Is this because it is not possible to do nested spying? Or is somehting wrong in my setup? Or should I approach this differently?
Details:
junit: 5.7.0
mockito: 3.12.4

When we look at the code presented, we see that calling classA.getSomeStuff() will return a new instance of ClassB with every call, i.e. classA.getSomeStuff() == classA.getSomeStuff() will evaluate to false.
What does that mean for our test setup? We construct a spy:
ClassB classB = spy(classA.getSomeStuff());
but this spy is never used. When we call classA.start(), it will call classA.getSomeStuff() again, and a new (non-spy) ClassB-instance is returned.
This is an example of code that is difficult to test: we have no easy way to control or verify the behaviour of the ClassB-instance used during the test. We normally avoid this problem by using the Inversion of Control principle (wikipedia.com). Whether this is applicable in this situation I cannot say. For this, I would have to see more of the code, and it would most probably be out of scope for StackOverflow.
As a quick and dirty fix, we can mock the call to classA.getSomeStuff() after we have created our mock:
#Spy private ClassA classA;
#Mock private ClassC classC;
...
#Test
void test1() {
ClassB classB = spy(classA.getSomeStuff());
when(classB.getResults()).thenReturn(classC);
when(classA.getSomeStuff()).thenReturn(classB);
classA.start();
}
As an aside: When using mockito, we should prefer the form
when(someMock.someMethod()).thenReturn(someResult);
over
doReturn(someResult).when(someMock).someMethod();
The former guarantees type safety at compile time, the latter does not. It is not always possible to use the former form (e.g. for methods returning void we have to use the latter form).

Related

Getting NullPointerException while calling nested method of Service class using mockito

I am writing JUnit test cases for my Service classes.
I have created dummy data to understand my scenario.
#Service
MainClass {
#Autowired
C c;
public void someServiceMethod(){
ResultClass someResult = c.getResult(string,string, int, int, List<String>,boolean);
}
}
#Service
public class C {
#Autowired
SomeRepository someRepository;
public ResultClass getResult(string,string, int, int, List<String>,boolean){
ABC returnSomeClassObject = someRepository.getSomeData(String,int,int);
}
}
#Test
MainClassTest {
#MockBean
SomeRepository someRepository;
when(someRepository.getSomeData(anyString(),anyInt(),anyInt())).thenReturn(SomeRepository);
//calling MainClass method
MainClass.someServiceMethod();
}
Class C's getSomeData() method returning ABC class object which is NULL and latter setting into another same class type object.
After setting value I am getting NULLPointerException as ABC is NULL.
Anybody have any idea where I am going wrong?
You are not returning expected object while writing mock statement
#Service
public class C {
#Autowired
SomeRepository someRepository;
public ResultClass getResult(string,string, int, int, List<String>,boolean){
ABC returnSomeClassObject = someRepository.getSomeData(String,int,int);
//Your return type should be ResultClass
// Where your return statement
// What is ABC?
}
}
#Test
MainClassTest {
#MockBean
SomeRepository someRepository;
when(someRepository.getSomeData(anyString(),anyInt(),anyInt())).thenReturn(SomeRepository);
// Why are you returning SomeRepository, This Should return object of ABC
#MockBean
ABC mockResultClass
when(someRepository.getSomeData(anyString(),anyInt(),anyInt())).thenReturn(mockResultClass);
//calling MainClass method
MainClass.someServiceMethod();
}
You are calling MainClass.someServiceMethod() which in turn calls getResult of class C. You should be mocking class C and using when-thenReturn on getResult() of C class if your intention is to test someServiceMethod() of Main class. Autowired will not work here since this is a Unit test and hence the instance of C c in Main class will be null.
Something like below:
#MockBean
C c;
when(c.getResult(anyString(), anyString(),anyInt(),anyInt(), any(List.class), anyBoolean()).thenReturn(someResult);
c.getResult(string,string, int, int, List<String>,boolean);
So, first of all, we need to be clear on what exactly you are unit testing. If you are trying to unit test someServiceMethod inside of MainClass, then that means you should NOT be also testing the functionality of someRepository. The idea is that each unit test should only be testing just that, a unit of code. So, to do that we need to use stubs as stand-ins for what would actually happen when you call methods owned by other classes. Then, we would write a differrent unit test just for someRepository.getSomeData() to confirm that it is also working as intended. In this manner, when get an error later down the line we will know exactly where we are encountering an issue.
Another issue I see is there is an apparent mismatch of return types for getResult() in C. The method says it returns a ResultClass, but when you call getSomeData you are expecting an ABC object. Either you left out the details where you convert the object back to a ResultClass, or that is a mistake. I'm going to assume the former unless you say otherwise.
With that in mind, let's write our test. Here's how I would write the test:
#RunWith(SpringRunner.class)
public class MainClassTest {
#Mock
C c;
#InjectMocks
MainClass mainClass;
#Test
public void testSomeServiceMethod {
ResultClass resultClass = new ResultClass(); //relevant constructor details here, mockbean, etc. you get the idea
//set any desired data for resultClass here
Mockito.when(c.getResult(anyString(), anyString(),
anyInt(), anyInt(), any(List.class), anyBoolean()))
.thenReturn(resultClass);
ResultClass newResult = mainClass.someServiceMethod();
//relevant test assertions here
}
}
As you can see, we are creating a ResultClass in the test, and telling Mockito to return that when getResult is called instead of what you would normally expect it to return. While the functionality may seem limited now, this is preferred as we only testing the MainClass and not the rest of our method calls.
In addition to this, we can (and should) write tests for getResult in C, and getSomeData in SomeRepository. I will leave it to you to write those tests.
EDIT: accidentally posted a bit early, fixing now.

How to mock classes with constructor injection

How to get constructor injection in Mockito
I have the following class:
class A {
private B mB;
A(B b) {
mB = b;
}
void String someMethod() {
mB.execute();
}
}
how do I test someMethod using a mock class A and class B using
B b = Mockito.mock(B.class)
Mockito.when(b.execute()).thenReturn("String")
A a = Mockito.mock(A.class)
//somehow inject b into A and make the below statement run
Mockito.when(a.someMethod()).check(equals("String"))
You need create real A class because you want to test it but you need to mock other classes used in A class. Also, you can find mockito documentation says that don't mock everything.
class ATest {
#Mock
private B b;
private A a;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
a = new A(b);
}
#Test
public String someMethodTest() {
String result = "result";
Mockito.when(b.execute()).thenReturn(result);
String response = a.someMethod();
Mockito.verify(b, Mockito.atLeastOnce()).execute();
assertEquals(response, result);
}
}
Another way of injecting a mock to real object (as A should be a real object) is to use annotations, they will create objects you want:
#Mock
B mockOfB;
#InjectMocks
A realObjectA;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
Then, like they said, run the method you want to test without mockito (because you want to test it, so call it on real instance) and check the result according to your expectations.
Behaviour of object B can be mocked in any way, to satisfy your needs.
You want to test someMethod() of class A. Testing the execute() of class B should take place in the other test, because instance of B is a dependency in your case. Test for execute() should be made in different test.
You don't need to test how B object will behave, so you need to mock it and afterwards, check that execute() was invoked.
So in your case your test will look something like this:
B b = Mockito.mock(B.class);
A a = new A( b );
a.someMethod();
Mockito.verify( b, Mockito.times( 1 ) ).execute();
In my opinion, you're mixing up two ways of testing.
If you want to write a test using Mockito, you just create a mock of some class and use it. This mock doesn't have anything related to a real object as you can (should) mock every method that is called in the test. That's why it doesn't make any sense to mock class B - it is simply not used by class A.
Otherwise, if you want to test a real behavior of class A then why do you want to mock it? Create a real instance of class A with a mocked instance of class B.
That's it! Don't mix it up.

Mocking autowired field in the method

I was wondering whether there's a way to mock the field which is being autowired.
Consider the following case.
I have a class name A
public class A {
#Autowired
private B b;
public void aMethod() {
b.method();
}
}
The other class B looks like this
public class B {
public void method() {
// some code
}
}
Now i want to write junit for the method.
I know there's a way to mock the autowired field like this.
public class TestA {
#InjectMock
private A a;
#Mock
private B b;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
public void testAMethod() {
// write test case.
}
}
But my question is -> Is there a way to mock the autowired field inside method after creating object with new.
eg
public class TestA {
public void testAMethod() {
A a =new A();
// how to mock the B instance in it.
}
}
Please suggest or there's no way to do this????
I dont want to change from private modifier. Nor i want to add getter's and setters or reflection. I just want to know is there a way to mock the B instance after creating the new object of A class.
How about ReflectionTestUtils?
A a = new A();
B b = mock(B.class);
ReflectionTestUtils.setField(a, "b", b);
It's still reflection-based and has all related drawbacks, though it's quite simple and easy to read.
You can not do that with mockito, this would require to modify the bytecode of the class being tested. However Powermock allows such stubs. Note though that I and the creator of Powermock - Johan Haleby - would push for a refactoring instead of using Powermock. Powermock is very powerful, maybe too much, and working allows anyone to write legacy code, that would be difficultly maintainable or extensible (property you can find in poorly designed legacy code).
In your case I don't know what's wrong in your case with the dependency injection. However if the code need a new instance of B, it may be useful to have inject in A a factory/provider/builder class which will make a new instance of B. Such code can be easily stubbed with Mockito.

How to mock another method in the same class which is being tested?

I am writing JUnit Test case for a class which has two methods methodA,methodB.
I would like to mock the call to the methodB from methodA in my test case
I am using spy on the class which I am testing, but still the methodB gets executed.
here is the class
public class SomeClass
{
public Object methodA(Object object)
{
object=methodB(object);
return object;
}
public Object methodB(Object object)
{
//do somthing
return object;
}
}
here is the test class
#RunWith( org.powermock.modules.junit4.legacy.PowerMockRunner.class )
#PrepareForTest(SomeClass.class)
public class SomeClassTest {
private SomeClass var = null;
#Before
public void setUp() {
var=new SomeClass();
}
#After
public void tearDown()
{
var= null;
}
#Test
public void testMethodA_1()
throws Exception {
Object object =new Object();
SomeClass spy_var=PowerMockito.spy(var);
PowerMockito.when(spy_var.methodB(object)).thenReturn(object);
Object result = var.methodA(object);
assertNotNull(result);
}
}
The method B still gets the called though I have mocked it
PLease suggest me a solution with the proper way of mocking the methodB call from methodA of the same class.
I ran into this yesterday, for spies is best to do:
doReturn(X).when(spy).method(any())
Taking this approach will result in brittle tests which will need to change if you refactor your class under test. I would highly recommend that you try to assert your expected test results by checking state of SomeClass rather than relying on mocks.
If you do indeed need to mock MethodB then this is an indication that maybe the behaviour in MethodB actually belongs in a separate class which you could then test the interaction of SomeClass with via mocks
if you do indeed need to do what you ask for then a PartialMock is what you want.
you probably want to create a partial mock of some class but indicate that calls to MethodA should call the actual method but then mock MethodB
You can see how to use them in the Mockito documentation
As stated in their documentation though Partial mocks are a code smell, though they have identified some explicit use cases.
As per #Totoro's answer, this is just a summary here.
Annotate the object for the class you are testing with the #Spy annotation.
So this:
#Spy
#InjectMocks
MyClassUnderTest myClassUnderTest;
instead of just
#InjectMocks
MyClassUnderTest myClassUnderTest;
Use doReturn() instead of when.thenReturn() whenever asking for the mocked object from the class spyed on (class under test).
doReturn(X).when(myClassUnderTest).method(any())
I had this very same challenge. Have a look at this solution which will work for newer testing suit versions.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>2.5.0</version>
</dependency>
Solution-
//when- then for MethodA test
SomeClass spy_var=Mockito.spy(var);
doReturn(X).when(spy_var).methodB(object)
Object result = spy_var.methodA(object);
//assertions on result

Mocking EJB injection in tests

Whenever I want to test a class which uses resource injection I end up including a constructor that will only be used within the test:
public class A {
#EJB
B b;
// Used in tests to inject EJB mock
protected A(B b) {
this.b = b;
}
public A() {}
// Method that I wish to test
public void foo() {
b.bar();
}
}
Is there another way of mocking resource injection or this is the correct pattern to follow?
you could use easy gloss to that effect, it mocks the EJBs injection system.
another way is to set the field using reflexion in your tests, I sometime use something like this :
public static void setPrivateField(Class<? extends Object> instanceFieldClass, Object instance, String fieldName, Object fieldValue) throws Exception {
Field setId = instanceFieldClass.getDeclaredField(fieldName);
setId.setAccessible(true);
setId.set(instance, fieldValue);
}
Eliocs,
If type B where an interface then you wouldn't "just" bo doing it for test-cases; you'd be allowing for any alternative implementations of "B's behaviour", even if the need for it/them hasn't been dreamed-up yet.
Yeah, basically that's the only pattern to follow (AFAIK)... so (rightly or wrongly) you may as well make the best of it ;-)
Cheers. Keith.
It's certainly one way to do it, although I'd rely on package access; don't provide a constructor injection point, but simply have your test in the same package as the bean being tested. That way, your test can just access the value directly (assuming it's not private):
#Test
public void EJBInjectionTest() {
A a=new A();
a.b=new B() {
// mock functionality here, of course...
};
assertNotNull(a.b);
}
According to this article (Mockito and Dependency Injection), Mockito has support for injecting mocked resources.
public class ATest
{
#InjectMocks
private A a; //this is your class under test into which the mocks will be injected.
#Mock
private B b; //this is the EJB to be injected.
#Before
public void setUp()
{
MockitoAnnotations.initMocks(this);
}
}
You can also inject multiple mocks. Just declare them in the same way as we did for B b.
The initMocks part can also be done in each test or in a BeforeClass setup method depending on your needs.

Categories

Resources