Pregunta

Suppose I have these two parts of XML Spring config in two distinct files;

//daoContext.xml
<bean id="myDao" class="com.MyDao"/>

//logicContext.xml
<bean id="myLogic" class="com.MyLogic">
    <constructor-arg ref="myDao"/><!--or other type of injection--?
</bean>

And there is the test class:

@ContextConfiguration("logicContext.xml")
public class BaseLogicTest extends AbstractTestNGSpringContextTests {
    @Autowired
    private MyLogic myLogic;

    @Test
    public void testMyTestable() {
        //...
    }
}

Now, what I want is to be able to mock MyDao class and inject is somehow into MyLogic which is to be injected in BaseLogicTest so I can use MyLogic with a mocked MyDao. Is this possible using Spring/Spring Test?

¿Fue útil?

Solución

The simplest solution is load all your xml. And manually replace dependence in test case.

@ContextConfiguration("logicContext.xml")
public class BaseLogicTest extends AbstractTestNGSpringContextTests {
    @Autowired
    private MyLogic myLogic;

    @Before
    public void injectTestDoubles() {
        myLogic.setMyDao(...);
    }

    @DirtiesContext
    @Test ...//test methods
}

But this corrupts the application context, so you need @DirtiesContext if you need "real" myDao in other test case sharing the same application context.

The most popular solution (my personal opinion :P ) is using mockito and a test-specific xml.

//daoContext.xml
<bean id="myDao" class="com.MyDao"/>

//logicContext.xml
<bean id="myLogic" class="com.MyLogic">
    <constructor-arg ref="myDao"/><!--or other type of injection--?
</bean>

//test-logicContext.xml
<bean id="myDao" class="org.mockito.Mockito" factory-method="mock">
    <constructor-arg value="com.MyDao" />
</bean>

@ContextConfiguration({"logicContext.xml", "test-logicContext.xml"})
public class BaseLogicTest extends AbstractTestNGSpringContextTests {
    @Autowired
    private MyLogic myLogic;

    @Autowired
    private MyDao myDao;//retrieve mock so you could define expectations

    @Test //test methods

    @After public void clearMocks() {
        Mockito.reset(myDao);//this is important if you have several test methods
    }
}

And this solution works for other mock framework like easyMock.

Otros consejos

You can do this by creating a FactoryBean to create the MyDao (or any) mock to be injected into your MyLogic instance.

public class FactoryBeanForMocks<T> implements FactoryBean<T> {
    private Class<T> mockClass;

    public FactoryBeanForMocks(Class<T> mockClass) {
        super();
        this.mockClass = mockClass;
    }

    @Override
    public T getObject() throws Exception {
        return Mockito.mock(mockClass);
    }

    @Override
    public Class<?> getObjectType() {
        return mockClass;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

}

Make the factory bean entry into your logicContext.xml so that a mock of MyDao can be injected into MyLogic:

//logicContext.xml
<bean id="myLogic" class="com.MyLogic">
    <constructor-arg ref="myDao"/><!--or other type of injection--?
</bean>
<bean id="myDao" class="x.y.z.FactoryBeanForMocks">
        <constructor-arg name="mockClass" value="x.y.MyDao"></constructor-arg>
</bean>

And this is your test class:

@ContextConfiguration("logicContext.xml")
public class BaseLogicTest extends AbstractTestNGSpringContextTests {
    @Autowired
    private MyLogic myLogic;

    //You can inject the mock myDao into it so that you can stub/verify method calls on it
    @Autowired
    private MyDao myDao;

    @Test
    public void testMyTestable() {
        //...
    when(myDao.process()).thenReturn("a");//stubbing myDao.process()
    assertEquals("a", myLogic.processRequest());// assuming myLogic.processRequest() calls myDao.process()
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top