Spring beans redefinition in unit test environment
I would propose a custom TestClass
and some easy rules for the locations of the spring bean.xml:
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = { "classpath*:spring/*.xml", "classpath*:spring/persistence/*.xml", "classpath*:spring/mock/*.xml"})@Transactional@TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, TransactionalTestExecutionListener.class, DirtiesContextTestExecutionListener.class})public abstract class AbstractHibernateTests implements ApplicationContextAware { /** * Logger for Subclasses. */ protected final Logger log = LoggerFactory.getLogger(getClass()); /** * The {@link ApplicationContext} that was injected into this test instance * via {@link #setApplicationContext(ApplicationContext)}. */ protected ApplicationContext applicationContext; /** * Set the {@link ApplicationContext} to be used by this test instance, * provided via {@link ApplicationContextAware} semantics. */ @Override public final void setApplicationContext( final ApplicationContext applicationContext) { this.applicationContext = applicationContext; }}
If there are mock-bean.xml
in the specified location, they will override all "real" bean.xml
files in the "normal" locations - your normal locations might differ.
But … I would never mix mock and non-mock beans, as it's hard to trace problems when the application grows older.
One of the reasons spring is described as test-friendly is because it may be easy to just new or mock stuff in the unit test.
Alternately we have used the following setup with great success, and I think it is quite close to what you want, I would strongly recommend it:
For all beans that need different implementations in different contexts, switch to annotation based wiring. You can leave the others as-is.
Implement the following set of annotations
<context:component-scan base-package="com.foobar"> <context:include-filter type="annotation" expression="com.foobar.annotations.StubRepository"/> <context:include-filter type="annotation" expression="com.foobar.annotations.TestScopedComponent"/> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan>
Then you annotate your live implementations with @Repository, your stub implementations with @StubRepository, any code that should be present in the unit-test fixture ONLY with @TestScopedComponent. You may run into needing a couple more annotations, but these are a great start.
If you have a lot of spring.xml, you will probably need to make a few new spring xml files that basically only contain the component-scan definitions. You'd normally just append these files to your regular @ContextConfiguration list. The reason for this is because you frequently end up with different configurations of the context-scans (trust me, you will make at least 1 more annotations if you're doing web-tests, which makes for 4 relevant combinations)
Then you basically use the
@ContextConfiguration(locations = { "classpath:/path/to/root-config.xml" })@RunWith(SpringJUnit4ClassRunner.class)
Note that this setup does not allow you to have alternating combinations of stub/live data. We tried this, and I think that resulted in a mess I wouldn't recommend anyone ;) We either wire inn the full set of stubs or the full set of live services.
We mainly use auto-wired stub dependencies when testing gui near stuff where the dependencies are usually quite substantial. In cleaner areas of the code we use more regular unit-testing.
In our system we have the following xml-files for component-scan:
- for regular web production
- for starting web with stubs only
- for integration tests (in junit)
- for unit tests (in junit)
- for selenium web tests (in junit)
This means we totally have 5 different system-wide configurations that we can start the application with. Since we only use annotations, spring is fast enough to autowire even those unit tests we want wired. I know this is untraditional, but it's really great.
Out integration tests run with full live setup, and once or twice I have decided to get really pragmatic and want to have a 5 live wirings and a single mock:
public class HybridTest { @Autowired MyTestSubject myTestSubject; @Test public void testWith5LiveServicesAndOneMock(){ MyServiceLive service = myTestSubject.getMyService(); try { MyService mock = EasyMock.create(...) myTestSubject.setMyService( mock); .. do funky test with lots of live but one mock object } finally { myTestSubject.setMyService( service); } }}
I know the test purists are going to be all over me for this. But sometimes it's just a very pragmatic solution that turns out to be very elegant when the alternative would be really really ugly. Again it's usually in those gui-near areas.
See this tutorial with @InjectedMock annotation
It saved me a lot of time. You just use
@MockSomeClass mockedSomeClass@InjectMockClassUsingSomeClass service@Beforepublic void setUp() { MockitoAnnotations.initMocks(this);}
and all your problems are solved. Mockito will replace the spring dependency injection with a mock. I just used it myself and it works great.