Doing Integration Testing on a multi-layer application
The application I'm working on is a pretty straightforward struts/spring web application: there is a database layer, a services (business classes) layer and a struts layer. All of these layers are seperate projects in my development group, each has its own application context xml file to wire them together and each compiles into a JAR file for usage in the main application. The thought being that the main application will get a JAR file and spring config file for each layer.
My problem is that I want to test each layer as a unit with as little coupling to the layers other than the one just below (for instance the WEB layer should know nothing about the DB layer and only work with the Services layer, etc.).
But, Spring when it loads the contexts doesn't care about these layers and needs to create (and be aware) of all the beans in total so even if the WEB layer XML file references only beans in the SERVICES XML file, the fact that the SERVICES beans reference the DB beans makes this a big issue. I essentally have to load all the lower level beans to do any testing on any layer.
What I would like is to be able to simply test that the beans in the layer I'm testing can be created and not worry about the lower levels. In a sense I want to stub out the dependancies or mock them in some way. I've tried a number of ways to do that but have had little luck.
My question is this. Since I'm sure others have build an app this way, how do they test in this situation? And I guess I'm also curious if my method seems sound.
Using AbstractDependencyInjectionSpringContextTests
If you're using AbstractDependencyInjectionSpringContextTests, you can override its getConfigLocations() method so that it returns :- the real config file(s) for the tier(s) you want to test
- fake config files for the tiers you want to mock or replace with test versions (e.g. a test database that could even be an in-memory one)
The other approach I've used is to load the lower-tier stubs/mocks into a StaticApplicationContext, then use that as the parent context for a FileSystemXmlApplicationContext that uses the real config files for the layer you're testing. Here's what I mean:
Code:
// Create a static application context to contain the mocks
StaticApplicationContext staticApplicationContext = new StaticApplicationContext();
// Create the mocks and register them with this application context
Foo mockFoo = createNiceMock(Foo.class);
staticApplicationContext.getBeanFactory().registerSingleton("foo", mockFoo);
staticApplicationContext.refresh();
// Make a new context based on the one containing the mocks, using the wiring information contained in the live XML files for the tier under test
String[] configFileNames = ... ;
ApplicationContext applicationContext = new FileSystemXmlApplicationContext(configFileNames, staticApplicationContext);
// Get beans from the "applicationContext" and test them as desired
...
HTH,