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;
}
Related
In the following case, can I test "MainServiceImpl"?
【What I want to do】
・Test target object is "MainServiceImpl".
・SubMainServiceImpl is used without mock.
・SubSubMainServiceImpl.subSubSayHello method is used with mock.
You may say "double autowired class is not recommended to mock." I know, but I want to know the above test is technically possible.
//Test Target Object
#Service
public class MainServiceImpl implements MainService {
#Autowired
private SubMainService subMainService;
#Override
public String mainSayHello() {
return "MainSayHello. Also..." + subMainService.subSayHello();
}
}
//MainServiceImpl depends on this class.
#Service
public class SubMainServiceImpl implements SubMainService {
#Autowired
private SubSubMainService subSubMainService;
#Override
public String subSayHello() {
return "SubSayHello. Also..." + subSubMainService.subSubSayHello();
}
}
//SubSubServiceImpl depends on this class.
#Service
public class SubSubMainServiceImpl implements SubSubMainService {
#Override
public String subSubSayHello() {
return "SubSubSayHello";
}
}
As a side note, I can mock direct-autowired class.
How can I mock "double" autowired class?
//Direct(not double) autowired class is mocked.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class MockMainServiceTest {
#InjectMocks
MainServiceImpl mainService;
#Mock
SubMainService subMainService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public void SubMainService_Mocked(){
doReturn("SubMainService_Mocked").when(subMainService).subSayHello();
Assert.assertThat(mainService.mainSayHello() ,is("MainSayHello. Also...SubMainService_Mocked"));
}
}
Technology
SpringBoot 2.5.4
JUnit 4.2
Java 1.8
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?
It is said in manual, that
The Test annotation tells JUnit that the public void method to which
it is attached can be run as a test case. To run the method, JUnit
first constructs a fresh instance of the class then invokes the
annotated method. Any exceptions thrown by the test will be reported
by JUnit as a failure. If no exceptions are thrown, the test is
assumed to have succeeded.
which may mean, that for each #Test method the context should be initialized again. This is also confirmed in this answer: https://stackoverflow.com/a/1564309/258483
Simultaneously, I see opposite in my experiment:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringJUnit4ClassRunnerDemo._Config.class)
public class SpringJUnit4ClassRunnerDemo {
public static class Bean1 {
{
System.out.println("Bean1 constructor called");
}
}
public static class Bean2 {
{
System.out.println("Bean2 constructor called");
}
private Bean1 bean1;
public Bean1 getBean1() {
return bean1;
}
#Autowired
public void setBean1(Bean1 bean1) {
this.bean1 = bean1;
System.out.println("Bean2.bean1 property set");
}
}
#Configuration
public static class _Config {
#Bean
public Bean1 bean1() {
return new Bean1();
}
#Bean
public Bean2 bean2() {
return new Bean2();
}
}
#Autowired
private Bean1 bean1;
#Autowired
private Bean2 bean2;
#Test
public void testBean1() {
assertNotNull(bean1);
System.out.println("testBean1() done");
}
#Test
public void testBean2() {
assertNotNull(bean2);
assertSame(bean2.getBean1(), bean1);
System.out.println("testBean2() done");
}
}
This code outputs
Bean1 constructor called
Bean2 constructor called
Bean2.bean1 property set
testBean1() done
testBean2() done
which may mean, that context is not initialized second time before second test.
What is actual and correct behavior and how to control it?
If you want the Spring context reloaded between test methods, you need to use the #DirtiesContext annotation: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html
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
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.