Nested method mocking in Mockito - java

I have the following Java classes:
public class A
{
#Autowired
private B b;
public int aFn()
{
int something = b.bFn();
}
}
public class B
{
#Autowired
private C c;
public int bFn()
{
int something = c.cFn();
}
}
public class C
{
public int cFn()
{
return 231;
}
}
And the following test using Mockito to test the above code:
public class test
{
#Autowired
private A a;
private C c;
#Test
public void testA()
{
c = mock(C.class);
when(c.cFn(),anyInt()).thenReturn(something);
assertEquals(0, a.aFn());
}
}
When I debug testA, I find that real c.Cfn() gets executed, not the mocked one.
Is there anything what I am doing incorrectly here?
Please help!

First of all, you should always mock the direct dependencies of an object, and not its transitive dependencies. So you should mock B, and not C, to test A. Then you would write a unit test for B by mocking C.
Second: you're not injecting the mock anywhere in the unit test. What you should have is:
public class Test {
// not autowired here
private A a;
private B mockB;
#Test
public void testA() {
mockB = mock(B.class);
when(b.bFn(), anyInt()).thenReturn(something);
// the missing part: injecting the mock into the tested object
a = new A(mockB);
// or a = new A();
// a.setB(mockB);
assertEquals(0, a.aFn());
}
}
When you use mock(B.class), you get one mock instance of B. That doesn't mean that all the other instances of B will do what the mock does.
Mocking C to test A is a bad practice: unit tests should test one class in isolation of the others. But if you really want that, then create a mock C, create a B and inject the mock C inside it, then create an A and inject the B inside it.
A --> B --> mockC

Forget whether what you want to mock is direct or nested.
Think of it from Spring's philosophical point of view.
What you are essentially want to do is mock a bean.
In your case, you have to mock bean for Class c using #MockBean annotations.
#RunWith(SpringRunner.class)
public class Test {
...
#MockBean private C c;
...
}
Please refer this article for details.

Related

how to mock the method call which is not done by some dependency of the class?

I am trying to write JUnits for some class which is as follows
class C{
public C fun(D d){
// fetch some data from database using object of D
// do some operations on that data
// create an object of C using this data
// return that object of C
}
}
#Component
class toBeTested{
#Autowired private A a;
#Autowired private B b;
public C methodToBeTested(){
C c = new C(a.getD()).fun();
return c;
}
}
I want to test methodToBeTested() method. Please tell is there any way In which we can mock the method calls of object D present in class C while execution of fun() method. Or If there is some other easy way to handle this situation in mockito. All I have found until now is this Stack overflow but still I am not clear how to use this in my situation
If you use #Autowired in class C, this C should be managed by Spring, otherwise the "autowiring magic of D inside C won't happen"
But if so, why do you create a new instance of C in class toBeTested? It doesn't really make sence.
So you should:
Place #Component on class C so that Spring will manage it.
Modify class toBeTested:
#Component
class toBeTested{
#Autowired private A a;
#Autowired private B b;
#Autowired private C c;
public C methodToBeTested(){
return c.fun();
}
}
Its quite strange to see c.fun() that returns itself, is it a builder or something? Maybe you should make it 'prototype scope' and then the solution is slightly different, but since its not mentioned in the question, I don't have enough details so I'll leave the answer as is...
Update 1
Now when its clear that class C for reasons which are beyond the control of OPs is not managed by spring, there are two options:
Use PowerMock/Power Mockito if you have to preserve class toBeTested in its current form (with new C() in the method to be tested. This approach in general considered a bad practice use it only if you don't have another choice. These tools can mock the creation of object (new) and substitute C with some stub on the fly.
If you can change the class toBeTested - use dependency injection:
public class ToBeTested {
#Autowired private A a;
#Autowired private B b;
#Autowired private CFactory cFactory;
public C methodToBeTested() {
return cFactory.create(a.getD()).fun();
}
}
interface CFactory {
C create(D d);
}
#Component
public class DefaultCFactoryImpl implements CFactory {
public C createC(D d) {
return new C(d);
}
}
This approach is much better because it allows to substitute the factory with the implementation that will create a mock of C or whatever you'll find appropriate for the test.
It's always better to use a constructor injection.
But if you don't want to refactor, you can use #InjectMocks from mockito.
class ToBeTestedTest {
#Mock private A a;
#Mock private B b;
#InjectMocks ToBeTested toBeTested; // mockito will inject your mocks
#Before
public void setUpMocks() {
when(a.getSomeParameter()).thenReturn(new DummyObject())
}
}

How to read variable from application.properties from method that is testing

I have class:
#Service
public class A {
#Value("${a.b.c}")
private String abc;
public void foo() {
sout(abc);
}
}
I Have test class:
#SpringBootTest
#SpringBootConfiguration
#RunWith(SpringRunner.class)
#TestPropertySource(locations = "classpath:application.yml")
public class TestA {
#Value("${a.b.c}")
private String abc;
#InjectMocks
private A a;
#Test
public void testFoo() {
this.a.foo();
}
}
When I debugging the test method testFoo(),
I see that variable abc is read from the application.yml file.
But,
inside the foo() method,
I see that the variable abc is null.
How can I set variable abc such that it is available in method foo() when I trying to test this method?
Step one is to answer this question: Am I unit testing the code in my class or am I integration testing the combination of Spring and some collection of code that includes my class?
If you are unit testing your code,
then it is not necessary to have Spring do its thing.
Instead,
you only need to instantiate your class,
set the values that Spring would have set for you,
execute the method you are testing,
then verify that your method executed correctly.
Here is your example unit test rewritten as I suggested:
public class TestA
{
private static final String VALUE_ABC = "VALUE_ABC";
private A classToTest;
#Test
public void testFoo()
{
classToTest.foo();
}
#Before
public void preTestSetup()
{
classToTest = new A();
ReflectionTestUtils.setField(
classToTest,
"abc",
VALUE_ABC)
}
}
Some Notes:
ReflectionTestUtils is part of Spring-test.
You don't need to use #InjectMocks because you have no mocks to inject.
I don't know what sout is, so I excluded it from the test. You should verify that the sout method was called with the correct value (in this case VALUE_ABC).
If you are just unit testing your code, you don't need Spring, which means that you don't need to use the #RunWith annotation.
You can try to overide the properties like that:
#TestPropertySource(locations = "location.properties",
properties = "a.b.c=123")
Example taken from here

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.

Mockito with Spring

For one of my use case, I have to mock an autowired dependency only a single test case, while I want other tests to use the original one.
public class A {
#Autowired
private B b;
}
Class TestA {
#Autowired
private A a;
void test1() {
//using B without mock.
}
void test2() {
// mock B b in A here
}
}
I want to mock the private class variable 'b' here in some particular tests. I know if I have to mock B in entire class I can use #Mock, #InjectMocks and MockitoAnnotations.initMocks(), but that will mock 'b' for the other test cases as well where I want original behavior.
You can simply create a mock object, and temporarily assign it to variable b. Something like
public void test1() {
//normal test
}
while in the other:
public void test2() {
try {
B autowiredObject = b; //saving the original reference
b = mock(B.class);
when(b.someGetFunction()).thenReturn("it is mocked");
//rest of your test
} finally {
b = autowiredObject;
}
}
Please note the finally clause. It is there to ensure that the class state (autowired dependency) gets restored during your test. This is a very important practice, otherwise a fail in your test could theorertically affect your other tests in the same class, which is something you should always avoid.
Usually, you don't use Spring for unit-testing but rely on mocking dependencies and inject them manually into the test-object. A simple test method therefore can look like this:
#Test
public void testSomethingWithA() throws Exception {
// Arrange
A sut = new A();
B mockedB = mock(B.class);
// inject B into A
Whitebox.setInternalState(sut, "b", mockedB);
// Act
Object retVal = sut.doSomething();
// Assert
assertThat(retVal, is(equalTo(someExpectedValue)));
}
If you want to inject a real object into A instead of a mocked object (for whatever reason) simply switch mockedB in Whitebox.setInternalState(...) with your real object.

Is there anything similar to Junit Setup Method in Mockito

I have the following scenario
interface DAO
{
String a();
String b();
String c();
}
I create a mock of this DAO interface and I feed it to something called DAOProcess. Inside DAOProcess, I have various methods calling DAO methods a, b and c.
Now each time I need to unit test a method in DAOProcess, I'll end up writing when(mockDAO.a()).thenReturn("test").
Is there anyway I can move these when(mockDAO.a()).thenReturn("test") common to all the test cases ?
If your test cases are all in one class you could make use of a method annotated with #Before, e.g.:
...
private DAO mockDAO;
#Before
public void setUp() {
mockDAO = mock(DAO.class);
when(mockDAO.a()).thenReturn("test");
...etc...
}
...
Or, if you need the behaviour over many test classes you could write a utility class to set behaviour on a Mock instance, e.g.:
public class MockDAOPrototype {
public DAO getMockWithDefaultBehaviour() {
final DAO mockDAO = mock(DAO.class);
when(mockDAO.a()).thenReturn("test");
...etc...
return mockDAO;
}
}
And then call MockDAOPrototype.getMockWithDefaultBehaviour() in your setUp method.
You can create an AbstractTestCase class that is abstract and is extended by all test cases where you need this mock. In that abstract test case, you will have the following statements.
#Ignore // just in case your runner thinks this is a JUnit test.
public abstract class AbstractTestCase
{
#Mock
private DAO mockDAO;
#Before
private void setupMocks()
{
when(mockDAO.a()).thenReturn("test")
....
}
}
In your concrete test case classes, you would
public class MyConcreteTestCase extends AbstractTestCase
{
#InjectMocks
#Autowired
private DAOProcess daoProcess;
....
}

Categories

Resources