How to mock behavior for Springockito mocks? - java

If I create a mock in my spring context file using Springockito as described here, how do I mock some behavior for it?
What I'm trying to do:
ClassA is being tested.
ClassB is autowired in ClassA.
ClassB is being mocked with Springockito.
ClassA needs ClassB to do something in its PostConstruct.
I need to mock ClassB to do that something, since it can't and shouldn't really do it.
Doing this is straight forward without using Springockito (using Mockito straight up), but I need to autowire these beans and use Spring in my tests. Any help is appreciated.

Note that new springockito-annotations help to achieve the same goal without messing with xml context and extra helper classes:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = SpringockitoContextLoader.class, locations = "classpath:test-config.xml")
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class MemoAutoTest extends AbstractJUnit4SpringContextTests {
#Autowired
private ClassA classA;
#Autowired #ReplaceWithMock
private ClassB classB;
#Test
public void testClassA() {
// stub B
when(classB.foo()).thenReturn(true);
when(classB.bar()).thenReturn(42);
// test A
}
}
This would cause ClassB to be replaced with mock at main application context initialization.

I'm not familiar with Springockito, but it looks interesting for some narrow cases - namely integration testing with mocking just a bit.
Anyway, it looks like for a straightforward use case you extend AbstractJUnit4SpringContextTests, you could also autowire ClassB in your test just like you do in ClassA. Then you could define your expected behavior for ClassB in your setup method.
But I think that you need to set up some behavior for the ClassB bean before you get access to it in your setup method. In that case, you may need another bean to set up ClassB to do the expected behavior. So your testContext.xml would have something like this in it:
<bean id="classA" class="com.jarvis.ClassA" depends-on="classBMockSetter" />
<mockito:mock id="classB" class="com.jarvis.ClassB" />
<bean id="classBMockSetter" class="com.jarvis.test.ClassBMockSetter">
<property name="classB" ref="classB" />
</bean>
The ClassBMockSetter would look something like:
public class ClassBMockSetter {
private ClassB classB;
public void setClassB(ClassB classB) {
this.classB = classB;
given(classB.foo()).willReturn(true);
given(classB.bar()).willReturn(42);
}
}
I think that would work, but at that point, isn't it easier to just hand-code your mock ClassB?

What is worked for me is using #InjectMocks notation.
(See https://bitbucket.org/kubek2k/springockito/wiki/Home)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = SpringockitoContextLoader.class, locations = "classpath:test-config.xml")
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class MemoAutoTest extends AbstractJUnit4SpringContextTests {
#Autowired
private ClassA classA;
#Autowired #InjectMocks
private ClassB classB;
#Test
public void testClassA() {
// stub B
when(classB.foo()).thenReturn(true);
when(classB.bar()).thenReturn(42);
// test A
classA.doSomethingThatInternallyCallClassBFoo();
}
}

Related

Mockito Throwing NullpointerException while calling doCallRealMethod

Am using Mockito version 3.6.28 for Junit testing. Am getting Nullpointer Exception while calling the real method on the object. Its because of the dependency on the target object is not injected correctly .This is the code am using.
public class ClassA{
#Autowired
LoggingService loggingService;
#Autowired
ClassB classB;
publc void doSomething(){
loggingService.info("info log"); // This will works fine
classB.doSomething();
}
}
public class ClassB{
#Autowired
LoggingService loggingService;
public void doSomething(){
loggingService.info("info log"); // Nullpointer on this line since loggingService is null
}
}
#RunWith(MockitoJUnitRunner.Silent.class)
public class TestClass{
#InjectMocks
ClassA classA;
#Mock
ClassB classB;
#Mock
private LoggingService loggingService;
#Test
public void testMethod(){
doCallRealMethod().when(classB).doSomething();
classA.doSomething();
}
}
Nullpointer Exceptions in Mockito are usually caused by missing dependencies as you said.
In your case ClassA's are injected accordingly, but when it comes to ClassB it is a Mock object ( i.e no dependencies are injected).
So you would have to inject the Mock's of ClassB while using ClassB.
Something like
#Test
public void testMethod(){
#InjectMocks
ClassB classB_1;
doCallRealMethod().when(classB_1).doSomething();
classA.doSomething();
}
The problem is that You are Injecting mocks only into ClassA and not into ClassB:
#InjectMocks
ClassA classA;
#Mock
private LoggingService loggingService;
#Mock
private ClassB classB;
So, the loggingService inside ClassB is null (it was not injected), and so it is throwing NullPointerException.
The thing is, if You are testing ClassA - most probably You should not call the real method of ClassB, You should mock it.
However if you have to do it, there is a way.
InelliJ is suggesting that instead of #Autowire it is better to use constructors (it is easy to do with lombok). When you have dependencies injected with constructor, it would be then much easier to pass a mock like:
private ClassB classB = new ClassB (mock(LoggingService.class));

Is creating the same beans for tests in #Configuation violating DRY?

I want to test class A that has a few dependencies from beans B and C. I wonder if copying a code of these beans from program code to test configuration violated dry?
P.S. I am completely new to testing and I do know how dumb the question is.
You don't need to copy code across from /main to /test. Spring provides a variety of ways to inject beans into tests.
First, you can use #SpringBootTest to load the Spring Context for your test and then instantiate your class under test using #Autowired
#SpringBootTest(classes = {ClassA.class, ClassB.class, ClassC.class})
public class ClassATest {
#Autowired
ClassA classA;
}
Alternatively you can mock your dependencies using Mockito (which comes as part of the Spring-Boot-Starter-Test package)
#ExtendWith(MockitoExtension.class)
public class ClassATest {
#Mock
ClassB mockB;
#Mock
ClassC mockC;
#InjectMocks
Class A classA;
}
As you mentioned you can also use #TestConfiguration to create a configuration for your test and inject those beans into your class using #Import and #Autowired
#TestConfiguration
public class ClassATestConfig {
#Bean
public ClassA classA() {
return new ClassA(classB(), classC());
}
#Bean
public ClassB classB() {
return new ClassB();
}
#Bean
public ClassC classC() {
return new ClassC();
}
}
Then inject the configured bean as so.
#ExtendWith(SpringExtension.class)
#Import(ClassATestConfig.class)
public class ClassATest {
#Autowired
ClassA classA;
}

Autowired fields of mocked object returns null in Mockito

I have a StudentService class. And I have written a class for unit testing methods of StudentService class. My code is as follows:-
#Component
#EnableAutoConfiguration
public class StudentService {
#Autowired
StudentInstitutionMapper studentInstitutionMapper;
public Integer getPresentStudentCount(StudentParams studentParam) {
// TODO Auto-generated method stub
StudentInstitutionExample example = new StudentInstitutionExample();
StudentInstitutionExample.Criteria criteria = example.createCriteria();
criteria.andActiveYnEqualTo("Y");
criteria.andDeleteYnEqualTo("N");
criteria.andIsPresentEqualTo("Y");
criteria.andInstitutionTestIdEqualTo(studentParam.getInstitutionTestId());
List<StudentInstitution> studentInstitutionList = studentInstitutionMapper.selectByExample(example);//line 8 in method
return studentInstitutionList.size();
}
}
And in my unit testing class, I have written following method.
#Test
public void testStudentService_getPresentStudentCount1()
{
StudentService service=new StudentService();
StudentParams studentParam=mock(StudentParams.class);
Integer institutionTestId=3539;
when(studentParam.getInstitutionTestId()).thenReturn(institutionTestId);
int i=service.getPresentStudentCount(studentParam);
assertEquals(0,i);
}
when I execute the test class, i get error. This is because in StudentService class, in getPresentStudentCount() method, at line 8, the studentInstitutionMapper field is null. This is happening only for mocked object. How do i get autowired fields of mocked object?
Try declaring the object studentInstitutionMapper like this in your test class.
#Mock
StudentInstitutionMapper studentInstitutionMapper;
You can inject autowired class with #Mock annotation. In many case you should create your test class instance with #InjectMocks annotation, thanks to this annotation your mocks can inject directly.
#RunWith(PowerMockRunner.class)
#PrepareForTest({StudentService.class})
public class StudentServiceTest {
#InjectMocks
StudentService service;
#Mock
StudentInstitutionMapper studentInstitutionMapper;
#Test
public void testStudentService_getPresentStudentCount1()
{
MockitoAnnotations.initMocks(this);
StudentParams studentParam=mock(StudentParams.class);
Integer institutionTestId=3539;
when(studentParam.getInstitutionTestId()).thenReturn(institutionTestId);
int i=service.getPresentStudentCount(studentParam);
assertEquals(0,i);
}
This would be help for better explanation: Difference between #Mock and #InjectMocks
There is one simple solution that doesn't not involve advanced annotations of mockito:
You can refactor the StudentService like this:
public class StudentService {
private final StudentInstitutionMapper studentInstitutionMapper;
public StudentService(StudentInstitutionMapper studentInstitutionMapper) {
this.studentInstitutionMapper = studentInstitutionMapper;
}
}
This solution is my favorite one because when I create the StudentService in test I see exactly what dependencies it requires, their type. So I can supply mocks/real implementation even without opening the source of StudentService class.
Another benefit of this type of injection (constructor injection as opposed to field injection that you've used in the question) is that nothing breaks encapsulation of the fields.
Notes:
I didn't put #Autowired on constructor because in recent version of spring its not required as long as there is a single constructor (and for unit tests its irrelevant at all).
If you're concerned about boilerplate code of constructor you can use Lombok and put an annotation for generating the all-args constructor for you. In conjunction with Note 1 this allows to drop the constructor code altogether
P.S. I do not intend to start the "holy-war" of field injection vs constructor injection here, I'm just stating this approach because no-one has mentioned it before in other answers and technically it solves the issue raised in the question. Feel free to google about this topic.
You need to use the #InjectMocks annotation in your unit test :
#ExtendWith(MockitoExtension.class)
class StudentServiceTest {
#Mock
private StudentInstitutionMapper studentInstitutionMapper;
#InjectMocks
private StudentService studentService;
#Test
public void testStudentService_getPresentStudentCount1() {
StudentParams studentParam = mock(StudentParams.class);
Integer institutionTestId = 3539;
when(studentParam.getInstitutionTestId()).thenReturn(institutionTestId);
int i = studentService.getPresentStudentCount(studentParam);
assertEquals(0, i);
}
}
You should also configure the behavior of the studentInstitutionMapper in the unit test to make it return the expected result.

Java Spring: Mocking Context in Multiple Files

Trying to make mocked Spring Context for unit tests purpose. Our Spring Configuration is pretty big and as for now trying to get this thing using Spring + Springockito Annotations.
Problem that I've stumbled upon is that I'd like to have multiple Java Classes taking care of the Context Creation/Mocking. As for now this looks like this (let's say Class a takes class B and List of ClassCs as constructor arguments):
//declaring context classes below
#ContextConfiguration(loader = SpringockitoAnnotatedContextLoader.class,
classes = {
ClassA.class,
ClassB.class,
SubClassCOne.class,
SubClassCTwo.class,
... //list goes on and on with more mocks
}
)
public class Configurator {
#Autowired
ClassA classA;
#ReplaceWithMock
ClassB classB;
#Autowired
List<ClassC> classesC;
#Autowired
SubClassCOne subclassCOne;
....
Problem is that List of C subclasses is far bigger than I'd like to have here (not to mention I'm putting whole responsibility on just one class), so I thought of another Class that will take care of creations like this:
ListProvider.java:
//THIS ANNOTATION WON'T BE TAKEN INTO ACCOUNT
#ContextConfiguration(loader = SpringockitoAnnotatedContextLoader.class,
classes = {
SubClassCOne.class,
SubClassCTwo.class,
...
}
)
public class ListProvider {
#Autowired
List<ClassC> classesC;
...
public List<ClassC> getClassesC(){
return classesC;
}
...
Configurator.java:
#ContextConfiguration(loader = SpringockitoAnnotatedContextLoader.class,
classes = {
ClassA.class,
ClassB.class,
ListProvider.class,
...
}
)
public class Configurator {
#Autowired
ClassA classA;
#ReplaceWithMock
ClassB classB;
#Autowired
ListProvider listProvider
#Autowired //probably not autowired anymore?
List<ClassC> classesC;
...
//then somewhere it'll take classesC from listProvider using getter?
But then the problem comes that even though ListProvider will be autowired correctly, it won't make use of #ContextConfiguration classes and it won't find any ClassC subclasses unless I won't provide everything in Configurator.java, which is exactly what I'd like to avoid.
Is there a change to split this Context Configuration up into multiple files?
Separate out your config into default and test using profiles:
#Configuration
#ComponentScan(basePackages = "com.greg")
public class MainConfig {
}
#Configuration
#Profile("test")
public class TestConfig {
}
Then run your test with a test profile
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=MainConfig .class)
#ActiveProfiles("test")
public class ATest {
So the test will pick up MainConfig at all time but will only pick up TestConfig when the profile is set to test. The #Profile can also be used to distinguish which beans are loaded on a scan.

How do I force autowiring in a JUnit test?

I'm using Spring 3.1.0.RELEASE and JUnit 4.8.1. I'm having trouble figuring out why a class' member field isn't getting autowired in a JUnit test. My test looks like ...
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "file:src/test/resources/testApplicationContext.xml" })
#TransactionConfiguration(defaultRollback=true)
#Transactional
public abstract class NowYouKnowEventsParserTest {
private EventFeed eventFeed;
#Before
public void setUp() {
eventFeed = getEventFeed(16);
} // setUp
#Test
public void testParser() {
Assert.assertNotSame(0, eventFeed.getEvents().size());
} // testParser
...
#Autowired
private EventFeedsDao eventFeedsDao;
protected EventFeed getEventFeed(final Integer id) {
return eventFeedsDao.findById(id);
} // getEventFeed
}
The class "EventFeed" invokes an instance of the below class ...
package com.myco.myproject.parsers;
...
public abstract class AbstractEventParser {
#Autowired
protected NetUtilsService netUtilsService;
...
}
but when it comes time, the AbstractEventParser's "netUtilsService" member field is null. This is strange because in my "testApplicationContext.xml" file, I have this, which I thought would take care of the autowiring ...
<mvc:annotation-driven />
<context:component-scan base-package="com.myco.myproject" />
How do I force autowiring in my JUnit test? I would prefer not to add and invoke a setter method for the member field but if that is the only way, so be it.
Is the EventFeed class managed by Spring, i mean is the EventFeed class annotated with either #Service or #Component. Also you need to do #Autowired of EventFeed in your test right. I am not seeing that in your AbstractParsetTest

Categories

Resources