I'm trying to write JUnit tests for some code that is normally managed with Spring.
Let's say I have this:
#Configurable
public class A {
#Autowired MyService service;
public void callA() { service.doServiceThings(); }
}
I can write a test for this class using Mockito and PowerMock like this:
#RunWith(PowerMockRunner.class)
public class ATest {
#Spy MyService service = new MyService();
#Before void initMocks() { MockitoAnnotations.initMocks(this); }
#Test void test() {
#InjectMocks A a = new A(); // injects service into A
a.callA();
//assert things
}
}
But now I run into a case when some other class constructs instances of A:
public class B {
public void doSomething() {
A a = new A(); // service is injected by Spring
a.callA();
}
}
How do I make service get injected into instances of A created inside a B method?
#RunWith(PowerMockRunner.class)
public class BTest {
#Spy MyService service = new MyService();
#Before void initMocks() { MockitoAnnotations.initMocks(this); }
#Test testDoSomething() {
B b = new B();
// is there a way to cause service to be injected when the method calls new A()?
b.doSomething();
// assert things
}
}
Field injection is bad but still there's one thing that you can do to easily stub that A instantiation (or maybe I misunderstood sth). Make B have an AFactory injected via constructor.
public class B {
private final AFactory aFactory;
public B(AFactory aFactory) {
this.aFactory=aFactory;
}
public void doSomething() {
A a = aFactory.getA();
a.callA();
}
}
And then you can create a Mock of aFactory and inject it to B via constructor.
Related
I want to test a method which creates an object of another class using it's parameterized constructor. Even though I've mocked the constructor of MyClass, it makes the third party library which is in constructor implementation and results in the error. I'm using Mockito/PowerMockito.
public class MyClass{
private MyObj obj;
MyClass (String profile)
{
//some 3rd party library call
obj = thridPartyLib.someMethod(profile);
}
public String someMethod(){
return obj.someExternalCall();
}
}
Class which I want to test
public class ClassTobeTested{
public void execute(){
//some code
// ......
MyClass myClass = new MyClass(profile);
myclass.someMethod();
}
}
What I tried so far - classUnderTest.execute() ends up calling the thridPartyLib.someMethod(profile); which is part of MyClass constructor.
#RunWith(PowerMockRunner.class)
#PrepareForTest(MyClass.class)
public class ClassTobeTestedTest {
private MyClass mockMyClass;
private ClassTobeTested classUnderTest;
#Before
public void init() {
classUnderTest = new ClassTobeTested();
mockMyClass = PowerMockito.mock(MyClass.class);
}
#Test
public void testExecute(){
PowerMockito.whenNew(MyClass.class)
.withArguments(Mockito.any())
.thenReturn(mockMyClass);
classUnderTest.execute();
}
}
Your code will work only if you are working with a spy or mock of classUnderTest. Try this. This should work
#RunWith(PowerMockRunner.class)
#PrepareForTest( {MyClass.class, ClassTobeTested.class })
public class ClassTobeTestedTest {
private MyClass mockMyClass;
private ClassTobeTested classUnderTest;
#Before
public void init() {
classUnderTest = spy(new ClassTobeTested());
mockMyClass = PowerMockito.mock(MyClass.class);
}
#Test
public void testExecute() throws Exception {
PowerMockito.whenNew(MyClass.class)
.withArguments(Mockito.any())
.thenReturn(mockMyClass);
classUnderTest.execute();
}
}
The pain might suggest another approach. Consider injecting a Factory into ClassTobeTested which knows how to create an instance of MyObj. For example:
class MyObjFactory {
MyObj create(String profile) {
return new MyClass(profile);
}
}
then
public class ClassTobeTested {
private final MyObjFactory factory;
public ClassTobeTested(MyObjFactory factory) {
this.factory = factory;
}
public void execute(){
//some code
// ......
// MyClass myClass = new MyClass(profile);
MyClass myClass = factory.create(profile);
myclass.someMethod();
}
}
so the unit test becomes simpler with only having to mock the Factory and have it return a mocked MyClass instance. Then it's simple to verify myclass.someMethod() was invoked as expected.
I recently learn to use PowerMock to write unit tests for a class called Module which extends class Base. They look like this.
public class Base {
protected final static ServiceA serviceA;
protected final static ServiceB serviceB;
static {
serviceA = ServiceA.getInstance();
serviceB = ServiceB.getInstance();
}
}
public class Module extends Base {
public DataA methodA() {
return serviceA.getDataA();
}
public DataB methodB() {
return serviceB.getDataB();
}
}
My unit tests look like this:
#PowerMockIgnore("javax.management.*")
#RunWith(PowerMockRunner.class)
#PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleTest {
private Module module;
#Mock
private ServiceA serviceA;
#Mock
private ServiceB serviceB;
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(ServiceA.class);
PowerMockito.when(ServiceA.getInstance).thenReturn(serviceA);
PowerMockito.mockStatic(ServiceB.class);
PowerMockito.when(ServiceB.getInstance).thenReturn(serviceB);
module = new Module();
// I spy it because it has other methods I need to mock
module = PowerMockito.spy(module);
}
#Test
public void methodATest() {
DataA dataA = new DataA();
PowerMockito.when(serviceA.getDataA()).thenReturn(dataA);
DataA data = module.methodA();
assertEquals(dataA, data);
}
#Test
public void methodBTest() {
DataB dataB = new DataB();
PowerMockito.when(serviceB.getDataB()).thenReturn(dataB);
DataB data = module.methodB();
assertEquals(dataB, data);
}
}
Everything looks straightforward but when I run ModuleTest, the methodBTest() doesn't pass. It seems that PowerMockito.when(serviceB.getDataB()).thenReturn(dataB) doesn't work and makes the real serviceB.getDataB() method invoked. So assertEquals(dataB, data) throws org.junit.ComparisonFailure.
If I put the methodBTest() before methodATest(), the methodATest() doesn't pass. Same reason.
If I put PowerMockito.when(serviceA.getDataA()).thenReturn(dataA) and PowerMockito.when(serviceB.getDataB()).thenReturn(dataB) in the setup(), everything works perfectly.
This borders me all day. Is there anyone knowing why this is happening and how to resolve it? I need the mocking statement written in the respective test methods because I may change the returned values.
Here's a solution involving (almost) no changes
#PowerMockIgnore("javax.management.*")
#RunWith(PowerMockRunner.class)
#PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleTest {
private Module module;
private static ServiceA serviceA = Mockito.mock(ServiceA.class);
private static ServiceB serviceB = Mockito.mock(ServiceB.class);
#BeforeClass
public static void oneTimeSetup() throws Exception {
PowerMockito.mockStatic(ServiceA.class);
PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);
PowerMockito.mockStatic(ServiceB.class);
PowerMockito.when(ServiceB.class, "getInstance").thenReturn(serviceB);
}
#Before
public void setup() throws Exception {
module = new Module();
// I spy it because it has other methods I need to mock
module = PowerMockito.spy(module);
}
#Test
public void methodATest() {
DataA dataA = new DataA();
Mockito.when(serviceA.getDataA()).thenReturn(dataA);
DataA data = module.methodA();
assertEquals(dataA, data);
}
#Test
public void methodBTest() {
DataB dataB = new DataB();
Mockito.when(serviceB.getDataB()).thenReturn(dataB);
DataB data = module.methodB();
assertEquals(dataB, data);
}
}
What was changed (and why):
In Base: serviceA and serviceB are changed to protected (Module can not access if private)
used "proper" (AFAIK) syntax for PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);
used a #BeforeClass and made serviceA and serviceB static to "bypass" static initialization in Base
Tested with Junit 4.12, PowerMockito 1.6.2.
Note: it's also possible to leverage #SuppressStaticInitializationFor to achieve the same goal:
#SuppressStaticInitializationFor(value = "so46196071.Base") // suppress the static in Base (note this is my package name)
#PowerMockIgnore("javax.management.*")
#RunWith(PowerMockRunner.class)
#PrepareForTest({Module.class, ServiceA.class, ServiceB.class})
public class ModuleBisTest {
private Module module;
#Mock
private ServiceA serviceA;
#Mock
private ServiceB serviceB;
#Before
public void setup() throws Exception {
// MockitoAnnotations.initMocks(this); /* this is not needed => done by the runner */
PowerMockito.mockStatic(ServiceA.class);
PowerMockito.when(ServiceA.class, "getInstance").thenReturn(serviceA);
PowerMockito.mockStatic(ServiceB.class);
PowerMockito.when(ServiceB.class, "getInstance").thenReturn(serviceB);
module = new Module();
Whitebox.setInternalState(Base.class, "serviceA", serviceA); // set serviceA in Base "by hand"
Whitebox.setInternalState(Base.class, "serviceB", serviceB); // set serviceB in Base "by hand"
// I spy it because it has other methods I need to mock
module = PowerMockito.spy(module);
}
// ...
I have class like the following:
public class Service {
public String method1 (class1, class2){
//do something
return result //Returns a string based on something
}
}
I want to test the class Service by calling the method 'method1' with mocked parameter (objects of Class1 & Class2). I don't have any idea, how to do this using Mockito.Can anyone help me with the initial push ?
If class1 and class2 are simple values or POJOs, you should just not mock them:
public class ServiceTest {
Service service;
#Before
public void setup() throws Exception {
service = new Service();
}
#Test
public void testMethod1() throws Exception {
// Prepare data
Class1 class1 = new Class1();
Class2 class2 = new Class2();
// maybe set some values
....
// Test
String result = this.service.method1(class1, class2);
// asserts here...
}
}
If class1 and class2 are more complicated classes, like services, it is strange to pass them as arguments... However I don't want to discuss your design, so I will just write an example of how you can do it:
public class ServiceTest {
Service service;
#Mock Class1 class1Mock;
#Mock Class2 class2Mock;
#Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
service = new Service();
}
#Test
public void testMethod1() throws Exception {
// Mock each invocation of the "do something" section of the method
when(class1Mock.someMethod).thenReturn(someValue1);
when(class2Mock.someMethod).thenReturn(someValue2);
....
// Test
String result = this.service.method1(class1Mock, class2Mock);
// asserts here...
}
}
I have below class scenario. While testing MyTestableClass, I wish to process Autowired class.
I would like to mock only variable in AutoWired class.
sample class is as below-
public class MyTestableClass {
#Autowired
private MyServiceClass service;
public void handleError(){
...
service.doSomething();
}
}
public class MyServiceClass {
#Autowired
private JMSChannel channel;
public void doSomething(){
System.out.println("Inside Service class");
.....
channel.isAvailable();
.....
}
}
#RunWith(MockitoJUnitRunner.class)
public class MyTestableClassTest {
private MyTestableClass testClass= new MyTestableClass();
private JMSChannel channel;
#Before
public void init(){
channel= mock(JMSChannel.class);
when(channel.isAvailable()).thenReturn(Boolean.TRUE);
}
#Test
public void test(){
testClass.handleError();
}
}
For example, Console should give me "Inside Service class" before returning true.
Thanks in Advance !
You need to create and instance of your service (or a mock of it) and set its channel to your mocked one, then set MyTestableClass#service to this one. Something like:
#Before
public void init(){
channel= mock(JMSChannel.class);
when(channel.isAvailable()).thenReturn(Boolean.TRUE);
MyServiceClass service = new MyServiceClass();
ReflectionTestUtils.setField(service, "channel", channel);
myTestableClass = new MyTestableClass();
ReflectionTestUtils.setField(myTestableClass, "service", service);
}
with ReflectionTestUtils from spring-test (NB: you can use a setter instead)
I have a FactoryBean (Spring) defined as follows:
public class AMockFactoryBean extends EasyMockFactoryBean<A>{
public AMockFactoryBean() {
super(A.class);
}
#Override
public A getObject() throws Exception {
MockA a= new MockA();
a.setB(createMock(B.class));
return new MockA();
}
}
The class A has an object of type B autowired:
public abstract class A {
#Autowired
protected B b;
}
MockA implements a few abstract classes and EasyMockFactoryBean utilizes the Spring FactoryBean method.
In my app.xml config I have:
<bean id="mockedA" class="AMockFactoryBean" />
My test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:testContext.xml")
public class ATest {
#Autowired
private A mockedA;
}
Result: mockedA in ATest is autowired correctly, but the autowired field A.b has been set to null by Spring. In debug mode I see how getObject() in AMockFactoryBean is called and how mockedA is given a Mock instance of EasyMock. But when the debugger jumps into the ATest class, mockedA.b is null. Why?
You return return new MockA(); instead of a. Your code should be
#Override
public A getObject() throws Exception {
MockA a= new MockA();
a.setB(createMock(B.class));
return a;
}