I want to test service A, which has a method methodA1, A refers to a service B, which has a method methodB1,
In methodB1 is called in methodA1,
#Service
class A{
#Autowired
B b;
void methodA1{
....
b.methodB1();
.....
}
}
#Service
class B{
void methodB1{
....
}
}
Now, I want to test methodA1, but the methodB1 need to be overridden, so I create a new class BMock;
#Service("bMock")
class BMock execute B{
#Override
void methodB1{
....
}
}
Test case like this:
class testClass extends springTest{
#Autowired
A a;
#Autowired
#Qualifier("bMock")
B b;
#Test
public void testMethodA1(){
a.methodA1();
}
}
actually, the methodA1 always call methodB1 in class B, I want it to call BMock in test case, how to do it?
If you have a setter for b in class A, override it explicitely in test :
class testClass extends springTest{
#Autowired
A a;
#Autowired
#Qualifier("bMock")
B b;
#Test
public void testMethodA1(){
a.setB(b);
a.methodA1();
}
}
If you have no setter (and do not want to create it), you can use reflection :
Field bField = A.getDeclaredField("b");
fField.setAccessible(true);
fField.set(a, b);
it breaks isolation of private fields but may be acceptable for a test.
Spring Re-Inject can be used to replace beans with mocks in test environment.
#ContextConfiguration(classes = {ReInjectContext.class, testClass.TextContext.class})
class testClass extends springTest {
#Autowired
A a;
// Spring context has a bean definition for B, but it is
// overridden in the test's constructor, so BMock is created
// instead of B. BMock gets injected everywhere, including A and the
// test
#Autowired
B b;
public testClass() {
// Replace bean with id "b" with another class in constructor
// "b" is bean ID that Spring assigns to B
ReInjectPostProcessor.inject("b", BMock.class);
}
#Test
public void testMethodA1(){
a.methodA1();
}
// If A and B are already the part of Spring context, this config
// is not needed
#Configuration
static class TestContext {
#Bean public A a() { return new A(); }
#Bean public B b() { return new B(); }
}
}
And remove #Qualifier and #Service from BMock
Related
I am testing a class that extends an abstract class. The concrete class members are Injected and when a method from the concrete class Mockito when-then returns null for the method call even though I have defined it in the test.
Abstract Class
public abstract class A {
#Inject
ObjA objA;
public String methodA() {
return objA.x()
}
// unimplemented methods
}
Class B
public class B extends A {
#Inject
ObjB objB;
public String methodB() {
String x = methodA()
return x;
}
}
TestClass
#RunWith(MockitoJUnitRunner.class)
public class BTest{
#Mock
ObjA objA
#Mock
ObjB objB
#InjectMock
B b;
#Before
public void init() {
Mockito.when(objA.x()).thenReturn("Hi");
}
#Test
public testBMethod() {
String x = b.methodB();
assertEquals(x,"Hi") // X is null even when, when then is defined for mocked object
}
}
Can someone let me know why the when-then didn't work?
I have Class A, which has a dependency on class B. And class B has again has a dependency on class C. Structure looks like:
Class A(){
#Autowired
private B b;
public void someMethod(){
b.callAnotherMethodAndGetValue();
}
}
Class B(){
#Autowired
private C c;
public void callAnotherMethodAndGetValue(){
c.callAnother();
}
}
Class ATest(){
#Spy
private B b;
public void someMethod(){
// it goes into this method, and throws null pointer exception at
// c.callAnother(); as c is null.
b.callAnotherMethodAndGetValue();
}
}
Is there any way I can let the stack flow to c.callAnother from Test Cases. Without doing when then at b.callAnotherMethodAndGetValue();
You need to mock C: and not spy on b:
#Mock
C c;
#InjectMocks
B b;
#Test
public void someMethod(){
b.callAnotherMethodAndGetValue();
}
Off cause you'd make your life easier if you used constructor injection!
I am writing the test cases for Class A. Inside Test For Class A , I should do #InjectMocks A a and should do a.someMethod() – Jyoti Yadav
Then the test should look like this:
#Mock
B b;
#InjectMocks
A a;
#Test
public void someMethod(){
a.someMethod();
}
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;
}
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.
I've got three classes A, B and C:
public class A {
#Autowired
private B someB;
private C someC = someB.getSomeC();
}
#Service
public class B {
C getSomeC() {
return new C();
}
}
public class C { }
Now if I write a unit test for A which looks like:
#RunWith(MockitoJUnitRunner.class)
public class ATest {
#InjectMocks
private A classUnderTest;
#Mock
private B someB;
#Mock
private C someC;
#Test
public void testSomething() {
}
}
Mockito is not happy with this:
org.mockito.exceptions.base.MockitoException:
Cannot instantiate #InjectMocks field named 'classUnderTest' of type 'class my.package.A'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : null
If I remove the call in class A, such that class A looks like the following:
public class A {
private B someB;
private C someC;
}
, Mockito is able to instantiate the classUnderTest and the test runs through.
Why is this the case?
edit: Using Mockito 1.9.5
Well this is always going to fail:
public class A {
private B someB;
private C someC = someB.getSomeC();
}
You're trying to call getSomeC() on a value which will always be null... that will always thrown NullPointerException. You need to fix A to handle the dependencies better. (Personally I'd take them as constructor parameters, but there are other options of course...)