This is my proof of concept project to implement Auto Mock Feature
in Spring Test Context Framework.
While writing tests with mock-frameworks, I have to explicitly prepare mock objects: such as instantiating mocks or annotating mocked variables, as well as resetting or re-assigning mocks between tests.
I think these code can be considered as infrastructure code for using mock-frameworks.
The Auto Mock Feature
is to automate these mocking infrastructure logic.
In other word, when this feature is enabled, automatically inject mock beans if dependency injection cannot resolve the target beans from user specified application configurations.
Sample:
@EnableMock // something like this to enable feature (TestBootstrapper)
@ContextConfiguration(...)
public class MyTest {
// test target bean, declared in bean config
@Autowired
MyController myController; // actual bean will be injected
// MyController has dependency to this bean but NOT declared in bean config for this test
@Autowired
MyService myService; // automatically mock will be injected
@Test
public void myTest(){
given(myService.getSomething).willReturn(...); // prepare mock
myController.doSomething(); // call test target method
... // verify logic
}
}
In order to create mock beans, I used hierarchical structure of BeanFactory. I created a MockBeanFactory which manages mocking beans, and place it to the root ApplicationContext's root BeanFactory in TestContext.
Since spring's DI mechanism searches candidate beans from child to parent context(bean factory), when child context(TestContext) cannot resolve the bean, the MockBeanFactory at last generates a mock bean and inject to the target variable.
In actual implementation, MockBeanFactory behaves bit different from normal BeanFactory.
It delays actual mock creation until bean retrieval time in order to gather all necessary information to decide whether to create a new mock or to return existing mock bean.
This is how BeanFactory(DefaultListableBeanFactory
) finds target bean:
- the child BeanFactory looks up candidate beannames at once including ancestors(parents)
- iterate candidate beannames to check whether each candidate qualifies for the injection target (such as qualifier name, etc)
- retrieve actual bean
MyController, FooService, BarService : Sample controller and services
MyTest : Sample controller unit test (not in web environment)
MyWebTest : Sample controller unit test (with web environment)
MockBeanFactory : BeanFactory implementation to manage mock beans
MockManager, MockInfo: Manage mock information
MockFeatureApplicationContextInitializer : ApplicationContextInitializer for this feature. (May be used/implemented as TestContextBootstrapper)
- make pluggable strategy to create/reset/(destroy) mocks
- improve MockBeanFactory impl
- expose mocking info to BeanFactory, so that user can inject the info to tests
- make this feature TestBootstrapper
- automatically reset mocks in each test
- etc.