/spring-mock-testcontext

Proof of Concept: Auto Mock Feature in Spring Test Context Framework

Primary LanguageJava

About

This is my proof of concept project to implement Auto Mock Feature in Spring Test Context Framework.

Feature Concept

Preface

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
  }
}

Implementation

Impl Concept

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.

MockBeanFactory Actual Impl

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.

Bean resolution logic in BeanFactory

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

Classes

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)

TODOs

  • 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.