Software Testing With Spring Framework

helpflightInternet και Εφαρμογές Web

10 Νοε 2013 (πριν από 3 χρόνια και 9 μήνες)

101 εμφανίσεις

Article
Software Testing With Spring Framework
Posted by
Srini & Kavitha Penchikala
on Nov 12, 2007 12:00 AM
Community
Java
Topics
Software Testing
,
Debugging
Tags
JUnit
,
Spring
,
Eclipse
,
JPA
Introduction
Testing is an integral part of enterprise software development. It is as important, i
f not the most important, as any other phase of the Software Development
Lifecycle (SDLC). But testing an enterprise application is easier said than done. The
re are several constraints that make testing effort in a project a major challenge.
These constraints usually fall into two categories: framework related constraints and
project methodology related constraints.
RelatedVendorContent
Hibernate without Database Bottlenecks
RESTful todo list sample tutorial with Groovy & Project Zero
Scale your applications without punishing your database
Delivering a Breakthrough Java ™ Computing Experience
A Technical Introduction to Terracotta
An example of framework related constraints is that J2EE architecture model does not
take into account the aspect of unit testing as part of the software
development. Since the container (application server) is the core component of J2EE r
un-time architecture, it is difficult to test applications built based on J2EE API,
outside the container. Unit testing (outside the container) is essential to achieve h
igh test coverage. It's also easy to reproduce many failure scenarios without the
overhead of application server setup and code deployment. It is vital to ensure that
tests can be run quickly which is important in development or production support
phases of the project. The task of verifying code using unit testing minimizes unprod
uctive time spent waiting for the application to be deployed every time we make
a code change.
Since the code in a traditional J2EE application depends heavily on the application s
erver, its functionality can be fully tested only when deployed inside a J2EE
container. But in-container testing is too slow and too much of an obstacle to develo
per productivity especially if the project is relatively large and number of code
artifacts (java source files) is high.
There are some J2EE frameworks that are built from ground-up to provide excellent sup
port for integrating software testing into the development process.
one of such java enterprise application development frameworks.
Recently, I did some consulting on an enterprise java application my wife worked on f
or a local Tier-One automotive supplier company. The project was to create a
Customer Profile Management System used to track the profiles of their customer compa
and JBoss 4.0 technologies. The project team followed an Agile software development a
pproach to deliver the requirements in one-week iterations. They used the
integration testing features offered by Spring framework, to test the application cod
e in Data Access and Service layers. We really liked the testing support provided
by Spring framework. It simplified testing to a great extent and made the aggressive
one-week development iterations possible and manageable.
This article provides an overview of the support provided by Spring framework in the
areas of unit and integration testing. I will use a sample loan processing web
application to help the readers in implementing an Agile Testing framework in a typic
al Java EE application and how to use Spring test classes to test the application
functionality.
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
1 of 26 01/04/2008 14:40
Agile Software Testing
A software development project must include good design and architecture practices as
well as good testing practices. An application may have a very good
architecture, design and code, but if it's not well tested, it cannot be considered a
successful product. Some companies (software service vendors) live and die by the
quality of their products and testing is crucial in the success of these companies.
Agile software development requires a comprehensive testing strategy in order to achi
eve the agility and quality in the software development project. Agile testing
includes unit testing as well as integration testing. This means that we should be ab
le to execute the tests as quickly as possible (one way to achieve the agility is to
run the tests outside the application server). Test Driven Development (
TDD
) is one of the key elements of Agile development process. Spring and other light-wei
containers such as
PicoContainer
and
HiveMind
provide great support for test driven software development.
Let's briefly look at the significance of unit and integration testing in a typical J
ava EE development project and objectives and constraints of each testing approach.
Unit Testing
Unit testing is used to test a specific unit (class) in the application. Unit tests s
hould be written to test all the methods in the class including all exception paths i
the methods. The objective of unit testing is to be able to quickly test any new code
or changes to existing code without the overhead and additional time involved
in tasks such as server configuration, services setup and application deployment. Dev
eloper unit testing is critical since it is easier and cheaper to find and fix bugs
earlier in software development lifecycle (at coding and unit testing phases) rather
than in the later stages.
JUnit
is the popular testing framework used for writing unit tests. In JUnit tests, we sim
ply instantiate the objects using
the container's JNDI resources and J2EE services such as resource pooling, JDBC conne
ction pools and JMS queues. We can also use testing techniques like
Objects
to test the code in isolation. With unit testing, there is no need for any infrastru
cture setup for an application server or even a database server.
There are some limitations to unit testing. Unit tests don't address the testing of f
unctional requirements of the application. These tests only cover testing of each
module in the application. Also, we can't test scenarios like asynchronous services w
hich require JMS message queues configured inside the application server. But
we should still be able to unit test as much application functionality as possible an
d use the in-container tests for only those functions that cannot be tested outside
the container.
Integration Testing
Unit tests are very useful to test a module or class in isolation. But it's also impo
rtant to do integration testing of the application to see how various modules would
work together when assembled in the integrated environment. Some functions that work
modules in the application. This scenario is very realistic in an agile development e
nvironment where different developers work on different parts of the application
at the same time and they need to merge code changes on a regular (in some developmen
between client and service layers of the application. Most of the integration tests u
sually run in the container. But to be truly agile, we will need to run atleast some
integration tests without any code deployment to the container.
Integration tests are useful in DAO layer where the implementations of DAO interfaces
cannot be unit tested effectively. Other objectives of integration testing is to
test aspects such as remote service, state (session) management, web flow and transac
longer time to run these tests. Since the application needs to be deployed inside Jav
a EE container, there is also the server setup and configuration overhead
involved in running these tests.
It should be noted that integration testing is complimentary testing, not a substitut
e for unit testing. Developers should first write sufficient unit tests for each java
class in the application to achieve a good code coverage. At the same time, there sho
uld be enough integration tests written to cover different use case scenarios in
the application that can not be tested with unit tests.
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
2 of 26 01/04/2008 14:40
There are several other types of testing in addition to unit and integration tests. F
ollowing table lists different testing strategies and their objectives.
Table 1. Java EE Testing Strategies
Test
Strategy
Objective
Unit Testing Test the application at class level to test all methods in each class.
Mock Objects Mock Objects are used in Client and Service layers of the application to test the cla
ss methods without having to really connect to back-end database or other network res
ORM Testing Verify the integrity of database table mappings defined in ORM layer. These test clas
ses use database metadata information to check ORM mappings.
DB Testing Test data access classes (DAOs) in isolation. These tests put the database tables int
o a known state for each test run.
XML Testing Test XML documents, their validity, and compare two different documents to assert if
they are same or not.
Integration
Testing
Test website navigation, web flow, and state (session) management and transaction man
agement.
Regression
Testing
Test the application functionality as an end-user would use the application when it's
deployed in production environment. These tests are usually run by a dedicated QA te
testing tools such as Mercury QuickTest Professional (
QTP
).
Load Testing Test the scalability of the application. These performance tests are usually run by a
dedicated load testing team using tools such as Mercury
Profiling Test for any memory leaks, memory usage, garbage collection etc at application run-ti
me. Developers run the application through a java profiler such as
Performance Tools Platform (
TPTP
).
There are a variety of open source testing frameworks to perform the various test str
ategies listed above. Following is a list of some of these frameworks.
JUnit
JMock
ORMUnit
DBUnit
XMLUnit
JUnitEE
MockEJB
Cactus
Since the testing aspect determines the success or failure of a project, any Java EE
framework we use for software development should provide the support for
seamless integration of testing into design and development phases. Let's look at som
e of the characteristics that an ideal Java EE framework should have, from
unit and integration testing stand-point.
Agile Development:
The framework should aid in an Iterative and Agile software development of the applic
ation. Agile methodologies are being adapted by more development teams
and agile testing and early feedback is the main part of iterative development.
Test Driven Development:
It is a well proven fact that the testing concern needs to be addressed right from th
e early phases of application development lifecycle. It is lot cheaper and more
effective to find and fix the bugs earlier in the process. And the best way to find a
ny bugs is by testing "early and often" in design and development phases of each
iteration in the project.
Interface based design:
One of the best practices that we object oriented programmers try to strive for is to
write java classes to interfaces instead of concrete classes. Writing to interfaces
gives us a great flexibility in running the unit and integration tests without having
to modify the client code whenever the implementation of a service component is
changed.
Separation of Concerns:
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
3 of 26 01/04/2008 14:40
We achieve a "separation of concerns" (SOC) when we consciously design and write code
trying to address (like domain, business logic, data access and infrastructure logic)
. This way different concerns like logging, exception handling and application
security can be tested in isolation without any dependencies on other modules.
Layered Architecture:
A typical Java enterprise application is designed in such a way that there are client
, service, domain and persistence layers. Any element of a layer should only
depend on other elements in the same layer or on elements of the layer just "beneath"
it (assuming that presentation is the topmost layer and persistence is the
bottom most layer in the architecture layers). What this means is the client layer ca
n only depend on service layer. Service layer can only depend on Domain layer
and domain layer can only depend on persistence layer. The Java EE framework should s
dependency on other layers.
Non-Invasive:
Frameworks like EJB and Struts force developers to extend framework specific classes
(like
application dependency on the specific framework which makes unit testing a challenge
and also leads to extra work when we need to switch to a different (and
better) framework. These frameworks are invasive in nature and should be carefully ch
osen with future extensibility requirements in mind. Note that the latest
version of EJB specification (version 3.0), which is part of Java EE 5, is less invas
ive as the Entities (formerly called Entity Beans) and Session beans are plain java
classes that can be tested outside the container similar to Spring beans.
Inversion Of Control (IoC):
The framework should provide support for Inversion of Control on the objects created
in the application. Inversion Of Control or IoC (also known as Dependency
Injection,
DI
) design pattern brings several benefits to integration testing. The primary benefit
on the container than one created using traditional J2EE application architecture.
Aspect Oriented Programming (AOP):
AOP
enables behavior, that would otherwise be scattered through different classes, to be
testing where we can test Java EE services such as transaction management and role ba
Service Oriented Architecture:
Testing is a critical component in a SOA infrastructure since a Service is used in di
fferent modules and applications in the enterprise. If a specific use case of a
service component is not tested completely, it could potentially cause production pro
blems and quality issues when code changes are implemented in the production
environment.
Data Access Abstraction:
A consistent architectural approach to data access is also very important in testing
the data access functions of an application. The data access abstraction should
be agnostic to any persistence implementation framework (such as Hibernate, JPA,
JDO
access exceptions thrown in the persistence layer.
Transaction management:
The framework should provide an abstraction interface for testing transaction managem
ent. It should integrate with
Container and Bean
managed transactions) and other transaction objects such as Hibernate
Spring Support For Testing
Spring framework was designed based on agile testing strategies to help developers fo
llow sound design and effective unit testing best practices. It also provides a
great support for running integration tests outside the application server. Spring is
a non-invasive framework in the sense that when we use Spring, there is a
minimal dependency of application code on the framework. We can configure application
specific classes (Note: When you use Spring template helper classes such as
JDBCTemplate
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
4 of 26 01/04/2008 14:40
dependency on the Spring framework). We can even configure any legacy classes that we
Spring framework in general, and Spring Testing module in particular support the foll
owing aspects:
Isolation:
Spring gives J2EE developers the flexibility to test java classes in isolation by inj
ecting mock implementations. For example, we can test a service class using a mock
implementation of the corresponding Repository class. This way, we can test business
logic in the service class without worrying about the persistence details of
connecting to a database.
Inversion of Control:
The framework provides sophisticated configuration management for POJOs. Spring IoC c
factory to instantiate application objects and wire them together using constructor o
r setter injection mechanisms.
Data Access:
It provides a nice persistence architecture for data access and a good hierarchy of d
ata access exceptions. It provides helper classes (such as
HibernateTemplate, TopLinkTemplate, JpaTemplate
) to work with the leading persistence frameworks.
Transaction Management:
Spring provides a nice abstraction framework for managing transactions (both local an
d global). This abstraction provides a consistent programming model in a wide
range of development environments and is the basis for Spring's declarative and progr
ammatic transaction management.
Integration Testing using Spring
Spring configuration, dependency injection (DI), data access (CRUD), and transaction
management are some of the concerns that we can test outside the server
environment using Spring Testing framework. The data access tests are executed agains
Spring context load time may not be an issue in small to medium size web applications
. But for a large size enterprise application, it could take significant time to
instantiate the classes in the application. Also, the overhead of running every singl
e test case in every single test fixture leads to slower overall test runs and
adversely affects developer productivity. Keeping these concerns in mind, Spring deve
lopment team wrote some test classes that can be used as integration tests
running outside the container. Since these test classes are extensions of JUnit
API, we get all the benefits of
test classes. These test classes setup a transaction for each test method and automat
ically cleanup (rolling back the transaction at end of each method) eliminating
the necessity for any database setup and teardown tasks.
Following is a list of items we can verify when we run integration tests in a Spring
application:
Loading of Spring contexts and management of the contexts by caching loaded contexts
application contexts by Spring IoC container.
Dependency Injection of test fixtures and Spring configuration details (To verify if
Convenience variables for data access and CRUD operations (The logic of data access c
Transaction management.
ORM mapping file configuration (Verify if everything related to a persistence object
We can run integration tests just like JUnit tests. They are slower to run compared t
o unit tests because we are testing the code at integration level rather than just
at a class level. But these integration tests are lot faster to execute than the test
s created using in-container testing frameworks such as
rely on deployment of the application to the container before the tests can be executed.
Spring integration test classes were designed to address various testing concerns so
there are different test classes in
Following table shows some of the test classes provided in Spring framework for integ
ration testing and in what scenarios they can be used.
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
5 of 26 01/04/2008 14:40
Table 2. Spring Test Classes
Test Class Name
AbstractDependencyInjection
SpringContextTests
This test class injects test dependencies so we don't need to specifically perform th
object in the set of configuration files specified in the
getConfigLocations()
AbstractTransactionalDataSource
SpringContextTests
This test class is used to test the code that runs inside a transaction. It creates a
there is a transaction present. It provides the fields such as a
JdbcTemplate
performed by application code. An ApplicationContext
is also inherited, and can be used for explicit lookup if necessary.
AbstractJpaTests This test class is used for testing JPA functionality. It provides an
EntityManager
AbstractAspectjJpaTests This class extends from AbstractJpaTests
and it is used for load-time weaving (LTW) purposes using
specify the location of AspectJ's configuration xml file.
AbstractModelAndViewTests
This is a convenient base class for testing the presentation and controller layers (u
Figure 1 below shows the class hierarchy diagram of Spring framework test classes ext
ending from JUnit
spring-mock.jar file (located in dist
folder inside Spring framework installation directory).
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
6 of 26 01/04/2008 14:40
Figure 1. Spring test class hierarchy (Click on the screen shot to open a full-size view.)
Following is a list of the factors to consider when deciding on which test class to e
xtend from:
To use the Spring application context without having to write the code to initialize
To test the data access (using data source)
To test a method inside a transaction (using transactionmanager)
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
7 of 26 01/04/2008 14:40
JDK Version: If you are using JDK 1.4 so you can't take the advantage of Annotations
The following section gives more details of these test classes.
AbstractSpringContextTests:
This is the base class for all Spring test classes. It provides convenience methods t
o load Spring application context. We extend this class when we need to test
loading of Spring context without having to explicitly manage the dependency injectio
n. It maintains a static cache of contexts by key which helps in significant
performance benefit if the application has several Spring beans (especially beans suc
h as LocalSessionFactoryBean for working with an ORM tool such as Hibernate)
to load. Hence, in most cases, it makes sense to initialize the application context o
nce and cache it for subsequent lookups.
AbstractSingleSpringContextTests:
This is an abstract test class that exposes a single Spring ApplicationContext
. It will cache the application contexts based on a context key which is usually the
config locations (String[]) describing the Spring resource descriptors. It encapsulat
es all the functions related to loading and managing the Spring context.
AbstractDependencyInjectionSpringContextTests:
This is a convenient super class for tests that depend on the Spring application cont
ext. It has
properties set by Dependency Injection. The default is AUTOWIRE_BY_TYPE
but it can also be set to
AbstractTransactionalSpringContextTests:
This class has several convenient methods to make the transaction management task eas
variables transactionManager, transactionDefinition, and transactionStatus
for managing the transactions in the test methods. It also has a method called
endTransaction() to force a commit or rollback of the transaction. And
startNewTransaction()
calling endTransaction().
AbstractTransactionalDataSourceSpringContextTests:
This is one of the mostly used Spring test classes. It provides useful inherited prot
ected fields, such as a
on the database. It also executes each test method in its own transaction, which is a
utomatically rolled back by default. This means that even if tests change the
database state (with insert, update, or delete), there is no need for a teardown or c
leanup script because the database changes are automatically rolled back. The
other helper methods in this class are countRowsInTable()
which is a handy method to check if a new record has been added or an existing recor
deleted, deleteFromTables is used to delete all rows from the tables,
executeSqlScript
back based on the state of the current transaction).
AbstractAnnotationAwareTransactionalTests:
This test class exposes SimpleJdbcTemplate
variable. Using this class, we can control transactional behavior using
@NotTransactional
to prevent any transaction being created at all (Note these two are Spring specific
framework. You can use this feature only if you are using JDK 1.5.
AbstractJpaTests:
This is base test class to extend from when we want to test a DAO class written using
JPA API. It exposes an
EntityManager for persistence methods. It requires DataSource and
JpaTransactionManager
Once you decide on which test class to extend, here are the steps required to integra
te Spring test classes in your integration tests:
Extend one of Spring test classes (usually
AbstractTransactionalDataSourceSpringContextTests
JPA functionality). Note that JPA is Java SE 5.0 only so if you are using pre-JDK1.5
Override getConfigLocations()
method to load all the configuration files for data source, transaction manager, and
code. Use @Override
annotation to specify that we are overriding this method defined in the super class
Write setter methods for all the DAO objects used in the test class (these DAO object
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
8 of 26 01/04/2008 14:40
option).
Add the test methods using these DAO objects (which in turn use the data source), tra
Rod Johnson's presentation on
System Testing Using Spring
is an excellent resource on the support Spring Test API offers in unit and integrati
Sample Application
The sample application used in this article is a home loan processing system. The use
case is to process the funding of a mortgage in the system. When a loan
application is submitted to a mortgage lending company, it first goes through the und
erwriting process where the underwriters approve or deny loan request based
on customer's income details, credit history and several other factors. If the loan a
pplication is approved it goes through the closing and funding processes.
Funding module in the loan processing application automates the process of disburseme
forwarding the loan package to a title company. The title company then reviews the lo
an package and schedules a date and time for closing the loan. Borrower and
the seller meet with the closing agent at the title company.
During closing, the buyer (borrower) and the seller read and sign the final loan docu
ments. The borrower would pay the down payment and money for any fees
associated with closing the loan. Also, closing costs and escrow fees are paid at the
time of the closing process. After the closing meeting, the title company sends
the signed agreement to the lender for funding. The lender transfers the funds to the
title company.
Application Architecture:
I followed the popular layered architecture in the design of the sample application.
These layers are Presentation, Application (Controller), Service, Domain and Data
Access layers. I named the data access classes (DAO) as Repositories following the na
ming conventions recommended in
the service, domain, and data access layers as plain java classes. I won't cover the
Presentation and Application layer classes in this article.
The application architecture diagram of LoanApp web application is shown in Figure 2
below.
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
9 of 26 01/04/2008 14:40
Figure 2. LoanApp application architecture diagram (Click on the screen shot to open
a full-size view.)
Data Model:
I created the loan application database (called LoanDB
) for the loanapp application using
with 3 tables called LOANDETAILS, FUNDINGDETAILS, and BORROWERDETAILS.
Domain Model:
There are three domain objects in the model, namely LoanDetails, BorrowerDetails
, and
funding module in the loan processing system.
Note: The model used in the sample application is just for demonstration purposes. A
real world application domain model would be more complex than what is
described here.
I wrote the data access classes for each of the domain classes. The DAO classes are
LoanDetailsRepositoryJpaImpl
and FundingDetailsRepositoryJpaImpl. And there is a service class called
FundingServiceImpl
calls the DAO's in order to approve, deny, or void the funding request for a specifie
d loan.
Persistence:
I used
Java Persistence API
(JPA) as the persistence model for object-relational mapping (ORM) requirements in t
the leading persistence frameworks such as Hibernate, JDO, iBATIS, TopLink, and
JPA
classes in org.springframework.orm.jpa package. I used the EntityManager
option (instead of JPA Template) for the persistence concern. This approach has
no dependency on Spring and still can be managed using Spring application context. We
can also take advantage of Annotations to inject
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
10 of 26 01/04/2008 14:40
@PersistenceContext and @PersistenceUnit tags.
Following table shows the frameworks and technologies used in the sample application.
Table 3. Technologies used in the sample application
Layer Technology Version
Controller Spring MVC 2.0
Service Spring 2.0
Domain Plain Java Classes
Persistence JPA
Database HSQLDB server 1.8.0
Java Java SE 6.0
Build Ant 1.7
IDE Eclipse 3.3
The other tools I used in the sample application are
Metrics
and
FindBugs
for static code analyses,
Testing
Here is a quick reiteration of what our objectives are in writing and executing the tests.
We want to code and run the tests without leaving the IDE (Eclipse).
There should be no special deployment of the code required
We should be able to exploit other code analysis tools such as Metrics and FindBugs r
those issues.
In addition to the traditional unit tests (using JUnit) for each of the Repository cl
asses in the application (namely
BorrowerDetailsRepositoryJpaImpl, and FundingDetailsRepositoryJpaImpl
), I also wrote integration tests to verify
Following table shows the list of the main and corresponding test classes.
Table 4: List of test cases in the loan application
Application Layer Main Class Test Class(es)
Data Access LoanDetailsRepositoryImpl LoanDetailsRepositoryJpaImplTest,
LoanDetailsRepositoryJpaImplSpringDITest
Data Access BorrowerDetailsRepository BorrowerDetailsRepositoryJpaImplTest
Data Access FundingDetailsRepositoryImpl FundingDetailsRepositoryJpaImplTest
Service FundingServiceImpl FundingServiceImplIntegrationTest,
FundingServiceImplSpringDITest,
FundingServiceImplSpringJPATest
To compare Spring's integration testing support, I first wrote the funding service in
tegration test without using Spring test classes (
Then I wrote two other test classes (FundingServiceImplSpringDITest and
FundingServiceImplSpringJpaTest
class, but this time using Spring test classes. I used several helper variables and m
ethods to aid in running the database queries. These are variables
jdbcTemplate, simpleJdbcTemplate, sharedEntityManager, and methods
countRowsInTable()
Let's take a look code examples of these unit and integration test classes to see how
many boiler-plate testing tasks are automated by Spring Testing API so the
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
11 of 26 01/04/2008 14:40
developers can focus on asserting the actual business logic.
First of all, let's look at the Spring configuration XML file. This file has the Spri
ng bean definitions for repository (DAO) classes used in the sample application. List
1 shows the code for loanApplicationContext-jpa.xml configuration file.
Listing 1. LoanApp Spring configuration details
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
12 of 26 01/04/2008 14:40
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="
http://www.springframework.org/schema/beans
"
xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance
"
xmlns:tx="
http://www.springframework.org/schema/tx
"
xsi:schemaLocation="
http://www.springframework.org/schema/beans

http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

http://www.springframework.org/schema/tx

http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
">
<!--
! Load JDBC Properties
!-->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.Proper
<property name="location" value="classpath:jdbc.properties"/>
</bean>
<!--
! Service classes
!-->
<bean id="fundingService" class="com.ideature.agiletestingspring.loanapp.service.Fun
<property name="loanDetailsRepository" ref="loanDetailsRepository"/>
<property name="borrowerDetailsRepository" ref="borrowerDetailsRepository"/>
<property name="fundingDetailsRepository" ref="fundingDetailsRepository"/>
</bean>
<!--
! Repository classes
!-->
<bean id="loanDetailsRepository" class="com.ideature.agiletestingspring.loanapp.repo
<bean id="borrowerDetailsRepository" class="com.ideature.agiletestingspring.loanapp.
<bean id="fundingDetailsRepository" class="com.ideature.agiletestingspring.loanapp.r
<!--
! Configure the JDBC datasource. Use the in-container datasource
! (retrieved via JNDI) in the production environment.
!-->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSo
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--
! Configure the entity manager.
!-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEnt
<property name="persistenceUnitName" value="LoanDBSpring"/>
<property name="dataSource" ref="dataSource"/>
<property name="loadTimeWeaver">
<!-- InstrumentationLoadTimeWeaver expects you to start the appserver with
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
13 of 26 01/04/2008 14:40
I wrote two base classes that all test classes in the sample application extend from.
These are
BaseJpaIntegrationTest.
BaseDataSourceSpringContextIntegrationTest:
This is the base test class for testing the data access and Spring context loading fu
nctions. It extends Spring's
AbstractTransactionalDataSourceSpringContextTests
class. It loads the application context by calling
this abstract test class is shown in Listing 2.
Listing 2. BaseDataSourceSpringContextIntegrationTest base test class
package com.ideature.agiletestingspring.loanapp;
import org.springframework.test.AbstractTransactionalDataSourceSpringContextTests;
public abstract class BaseDataSourceSpringContextIntegrationTest extends AbstractTra
private static final String[] configFiles = new String[]{"loanapp-applicationContext
@Override
protected String[] getConfigLocations() {
return configFiles;
}
}
BaseJpaIntegrationTest:
This is the base class for all the integration tests created to test ORM functions us
ing JPA. It extends Spring's
code for BaseJpaIntegrationTest class.
Listing 3. BaseJpaIntegrationTest test class
package com.ideature.agiletestingspring.loanapp;
import org.springframework.test.jpa.AbstractJpaTests;
public class BaseJpaIntegrationTest extends AbstractJpaTests {
private static final String[] configFiles = new String[]{"loanapp-applicationContext
@Override
protected String[] getConfigLocations() {
return configFiles;
}
}
The details of the other test classes in LoanApp application are as follows:
LoanDetailsRepositoryJpaImplTest:
This is the plain vanilla repository unit test class to test CRUD logic in
LoanDetailsRepositoryJpaImpl
retrieves loanDetailsRepository
from context, and then calls CRUD methods in the repository class. It also calls the
added in LOANDETAILS table. This test class also has setUp() and tearDown()
methods to initialize and cleanup the resources used in the test methods.
LoanDetailsRepositoryJpaImplSpringDITest:
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
14 of 26 01/04/2008 14:40
This test class is similar to LoanDetailsRepositoryJpaImplTest
but it uses Spring test classes to make it very easy to test data access methods in
LoanDetailsRepository class. It extends
BaseDataSourceSpringContextIntegrationTest
Spring's IoC container will inject the correct implementation of the repository inter
face at runtime. There is no boiler-plate code such as initializing the application
context, or setUp() and tearDown()
methods. Also, there is no need to call the delete method since all the DB changes a
each test method. We use AUTOWIRE_BY_TYPE (default option) to auto-wire
LoanDetailsRepository
FundingServiceImplIntegrationTest:
This is the test class for FundingServiceImpl
class. It shows how much code we have to write if we were not taking the advantage o
shows the code of this integration test class.
Listing 4. Sample code for FundingServiceImplIntegrationTest
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
15 of 26 01/04/2008 14:40
package com.ideature.agiletestingspring.loanapp.service;
import static org.junit.Assert.assertEquals;
import java.util.Collection;
import java.util.Date;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.ideature.agiletestingspring.loanapp.LoanAppConstants;
import com.ideature.agiletestingspring.loanapp.LoanAppException;
import com.ideature.agiletestingspring.loanapp.domain.BorrowerDetails;
import com.ideature.agiletestingspring.loanapp.domain.FundingDetails;
import com.ideature.agiletestingspring.loanapp.domain.LoanDetails;
import com.ideature.agiletestingspring.loanapp.dto.FundingDTO;
import com.ideature.agiletestingspring.loanapp.repository.BorrowerDetailsRepository;
import com.ideature.agiletestingspring.loanapp.repository.FundingDetailsRepository;
import com.ideature.agiletestingspring.loanapp.repository.LoanDetailsRepository;
import com.ideature.agiletestingspring.loanapp.repository.RepositoryException;
public class FundingServiceImplIntegrationTest {
private static final Log log = LogFactory.getLog(FundingServiceImplIntegrationTest.c
private static final String[] configFiles = new String[] {
"loanapp-applicationContext-jpa.xml"};
private ApplicationContext ctx = null;
private LoanDetailsRepository loanDetailsRepository = null;
private BorrowerDetailsRepository borrowerDetailsRepository = null;
private FundingDetailsRepository fundingDetailsRepository = null;
private FundingService fundingService;
@Before
public void setUp() {
ctx = new ClassPathXmlApplicationContext(configFiles);
log.debug("ctx: "+ctx);
loanDetailsRepository = (LoanDetailsRepository)ctx.getBean("loanDetailsRepository");
borrowerDetailsRepository = (BorrowerDetailsRepository)ctx.getBean("borrowerDetailsR
fundingDetailsRepository = (FundingDetailsRepository)ctx.getBean("fundingDetailsRepo
log.debug("loanDetailsRepository: "+loanDetailsRepository);
fundingService = (FundingService)ctx.getBean("fundingService");
log.debug("fundingService: " + fundingService);
}
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
16 of 26 01/04/2008 14:40
As you can see in testLoanFunding()
method, we have to explicitly call the delete methods in
was before running this test.
FundingServiceImplSpringDITest:
This class extends BaseDataSourceSpringContextIntegrationTest
base class. It has the setter methods for Repository objects so these will be inject
Spring DI container when the application context is loaded. The source code for this
integration test class is shown in Listing 5.
Listing 5. FundingServiceImplSpringDITest test class
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
17 of 26 01/04/2008 14:40
package com.ideature.agiletestingspring.loanapp.service;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.internal.runners.TestClassRunner;
import org.junit.runner.RunWith;
import com.ideature.agiletestingspring.loanapp.BaseDataSourceSpringContextIntegration
import com.ideature.agiletestingspring.loanapp.LoanAppConstants;
import com.ideature.agiletestingspring.loanapp.domain.LoanDetails;
import com.ideature.agiletestingspring.loanapp.repository.LoanDetailsRepository;
import com.ideature.agiletestingspring.loanapp.repository.RepositoryException;
@RunWith(TestClassRunner.class)
public class FundingServiceImplSpringDITest extends BaseDataSourceSpringContextIntegr
private static final Log log = LogFactory.getLog(FundingServiceImplSpringDITest.cla
private LoanDetailsRepository loanDetailsRepository = null;
public void setLoanDetailsRepository(LoanDetailsRepository loanDetailsRepository) {
this.loanDetailsRepository = loanDetailsRepository;
}
@Before
public void initialize() throws Exception {
super.setUp();
}
@After
public void cleanup() throws Exception {
super.tearDown();
}
@Test
public void testFindLoans() throws RepositoryException {
// First delete all the records from LoanDetails table
// by calling deleteFromTables() helper method.
deleteFromTables(new String[]{"LoanDetails"});
Collection loans = loanDetailsRepository.getLoans();
assertEquals(0, loans.size());
}
@Test
public void testJdbcQueryUsingJdbcTemplate() {
// Use jdbcTemplate to get the loan count
int rowCount = jdbcTemplate.queryForInt("SELECT COUNT(0) from LoanDetails");
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
18 of 26 01/04/2008 14:40
The helper method deleteFromTables() is used in this class to delete the data from
FUNDINGDETAILS
class. I also used jdbcTemplate variable in one case and countRowsInTable()
in another instance, to get the row count from the specified table.
FundingServiceImplSpringJpaTest:
This class extends BaseJpaIntegrationTest
base class to take the advantage of the convenience methods that the super class pro
simpleJdbcTemplate helper variable to get the row count from FUNDINGDETAILS
table. I also run a test against an invalid query using
on sharedEntityManager
attribute. Listing 6 shows the source for FundingServiceImplSpringJpaTest class.
Listing 6. FundingServiceImplSpringJpaTest class
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
19 of 26 01/04/2008 14:40
package com.ideature.agiletestingspring.loanapp.service;
import java.util.Collection;
import java.util.Date;
import javax.persistence.EntityManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.internal.runners.TestClassRunner;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.ExpectedException;
import com.ideature.agiletestingspring.loanapp.BaseJpaIntegrationTest;
import com.ideature.agiletestingspring.loanapp.LoanAppConstants;
import com.ideature.agiletestingspring.loanapp.domain.BorrowerDetails;
import com.ideature.agiletestingspring.loanapp.domain.FundingDetails;
import com.ideature.agiletestingspring.loanapp.domain.LoanDetails;
import com.ideature.agiletestingspring.loanapp.repository.BorrowerDetailsRepository;
import com.ideature.agiletestingspring.loanapp.repository.FundingDetailsRepository;
import com.ideature.agiletestingspring.loanapp.repository.LoanDetailsRepository;
import com.ideature.agiletestingspring.loanapp.repository.RepositoryException;
@RunWith(TestClassRunner.class)
public class FundingServiceImplSpringJpaTest extends BaseJpaIntegrationTest {
private static final Log log = LogFactory.getLog(FundingServiceImplSpringDITest.clas
private LoanDetailsRepository loanDetailsRepository = null;
private BorrowerDetailsRepository borrowerDetailsRepository = null;
private FundingDetailsRepository fundingDetailsRepository = null;
public void setLoanDetailsRepository(LoanDetailsRepository loanDetailsRepository) {
this.loanDetailsRepository = loanDetailsRepository;
}
public void setBorrowerDetailsRepository(BorrowerDetailsRepository borrowerDetailsR
this.borrowerDetailsRepository = borrowerDetailsRepository;
}
public void setFundingDetailsRepository(FundingDetailsRepository fundingDetailsRepos
this.fundingDetailsRepository = fundingDetailsRepository;
}
@Before
public void initialize() throws Exception {
super.setUp();
}
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
20 of 26 01/04/2008 14:40
The transaction is rolled back at the end of testInsertLoanDetails()
method. This is why even though we call
the database insert is undone when the transaction is rolled back. This way we don't
have to worry about leaving any test data in the tables after the integration
tests are executed or run any special DB cleanup scripts to delete the test data crea
ted during the tests.
To test transaction state, testLoadLoanDetails()
method calls transaction manager helper methods
the current transaction and get a new transaction respectively. The new transaction s
tarts a new Unit Of Work (UOW) where the
doesn't exist in the system. These helper methods are useful for testing lazy loading
feature in ORM tools such as
These helper methods work in pre-Java 5 applications as well.
AllIntegationTests:
Finally, there is AllIntegationTests
test suite that runs all Funding Service related tests in one execution. Listing 7 s
Listing 7. FundingServiceImplSpringJpaTest class
package com.ideature.agiletestingspring.loanapp;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
import com.ideature.agiletestingspring.loanapp.service.FundingServiceImplIntegrationT
import com.ideature.agiletestingspring.loanapp.service.FundingServiceImplSpringDITest
import com.ideature.agiletestingspring.loanapp.service.FundingServiceImplSpringJpaTes
import com.ideature.agiletestingspring.loanapp.service.FundingServiceImplTest;
@RunWith(Suite.class)
@SuiteClasses(value = {
FundingServiceImplTest.class,
FundingServiceImplIntegrationTest.class,
FundingServiceImplSpringDITest.class,
FundingServiceImplSpringJpaTest.class
})
public class AllIntegrationTests {
}
To execute these tests make sure the configuration file (
loanapp-applicationContext-jpa.xml
logging to verify that Spring beans are being loaded by the application context. Look for
CachedIntrospectionResults
entity manager, datasource, transaction manager and other objects required for runnin
g the integration tests.
Conclusions
Spring makes test driven J2EE application development a reality by offering Java EE d
evelopers an easy and powerful framework to write and run both unit and
integration tests outside the container. Its non-invasive configuration management, d
ependency injection to use mock objects, and consistent abstraction over
hard-to-stub APIs makes unit testing outside the container easy. Its testing module u
ses Dependency Injection (DI) and Aspect Oriented Programming (AOP)
techniques to create a foundation on which we can build our unit and integration tests.
Some of the best practices of writing tests using Spring test classes are as follows:
Make sure you have the same Spring configuration files in the integration tests as in
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
21 of 26 01/04/2008 14:40
differences that may cause problems when the application is deployed in the productio
There are some differences that are related to database connection pooling and transa
you are deploying to a full-blown application server, you will probably use its conne
production you will use a JndiObjectFactoryBean for the DataSource, and
JtaTransactionManager
container integration tests, so we should use Commons DBCP
BasicDataSource
HibernateTransactionManager for these tests.
Spring's integration testing support is not a replacement for the real regression tes
get to how the end-users will use the application when it's implemented in the produc
We should make it a practice to think and plan about testing and involve QA team righ
t from the beginning of a project. We should write unit tests to cover as many
as scenarios and paths (including exception paths) in the main class. Test Driven Dev
elopment (TDD) is a great way to achieve the desired test coverage and
production quality in the code we write in the project. If we can't get this TDD goin
g in our projects, we should atleast try the other TDD (Test During Development)
and make sure the code is unit tested before it's deployed to the integration environ
ment (usually Test environment).
Resources
Spring Testing Documentation
Spring in Action, Second Edition
, Craig Walls with Ryan Breidenbach, Manning Publications
System Integration Testing Using Spring
, Rod Johnson, The Spring Experience 2006.
Java Persistence API
Spring JPA Documentation
where to start? Spring - Java has a perfectly good dependency injection framework bui
lt in. Its called 'new'. Extremely weak naming of tests - Looking at the test
name in isolation give you no idea what the test is trying to prove. Needless logging
in tests - Just what is the point - if you care about something then you can
10 comments
there is just so much wrong with that article & the code in it.
by James Richardson Posted Nov 13, 2007 10:38 AM
Re: there is just so much wrong with that article & the code in it.
by Nathan Hughes Posted Nov 14, 2007 12:28 PM
Re: there is just so much wrong with that article & the code in it.
by Morgan Creighton Posted Nov 14, 2007 1:20 PM
new as dependency injection by James Richardson Posted Nov 14, 2007 5:29 PM
Re: new as dependency injection by Tim Andersen Posted Nov 27, 2007 1:18 PM
Re: new as dependency injection
by James Richardson Posted Nov 28, 2007 10:42 AM
Re: new as dependency injection by Y Z Posted Feb 24, 2008 9:08 AM
Re: there is just so much wrong with that article & the code in it.
by Luc Dewavrin Posted Nov 18, 2007 9:04 AM
fully worked example by James Richardson Posted Nov 18, 2007 5:28 PM
Tear down the wall! by Michael James Posted Nov 20, 2007 10:46 PM
there is just so much wrong with that article & the code in it.
Nov 13, 2007 10:38 AM by James Richardson
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
22 of 26 01/04/2008 14:40
assert() it. Extremely poor class naming - LoanDetailsRepositoryJpaImpl (!) No generi
cs - You're using Java 5 constructs: @Test, but then reverting to 1.4 typeless
collections. No content testing - You persist fields into the database, but then dont
check that you load then back in correctly. The content testing you do do
doesn't purge the entity from memory - thus making it meaningless. Bad data type mode
UnitOfWork - but then don't abstract it - making it very unclear as to whats going on
. Suggestions: Use simple constructors to "Repositories" that take an entity
manager. Create EntityManagers simply using java in your test code. Use transactional
aware wrappers that allow your tests to work in the correct transactional
context. Use the Builder pattern to reduce the amount of set up code for the items yo
u create. Use sensible names for your classes & methods: not LoanDetails,
but Loan not loanDetailsRepository.insertLoanDetails(..), but persistentLoans.add(loa
n) not LoanDetailsRepositoryJpaImpl but PersistentLoans not
testLoanFunding, but testCanAddALoanAndFindItByItsPrimaryKey Compose don't inherit -
you an extremely powerful set of assertions. An example (also look no spring!):
public void setUp() throws SQLException, EnvironmentException {
SystemConfiguration systemConfiguration = SystemConfiguration.load();
DatabaseCleaner.forAllTables(systemConfiguration).clean();
entityManager = TestConfiguration.createEntityManager(systemConfiguration);
transactomatic = new Transactomatic(entityManager);

todoList = new PersistentTodoList(entityManager);
}
public void testCanAddAMaturingLoanAndFindItByItsPrimaryKey() throws Exception {
final int remoteSystemId = 765;
final AbstractMaturingLoanTodoItem item = new PositionLevelMaturingLoanTodoIt
.withRemoteSystemId(remoteSystemId)
.build();
transactomatic.perform(new UnitOfWork() {
public void work() {
todoList.addItem(item);
}
});
transactomatic.perform(new UnitOfWork() {
public void work() throws Exception {
AbstractMaturingLoanTodoItem found = (AbstractMaturingLoanTodoItem) t
assertThat(found, SamePublicFieldsMatcher.samePublicFieldsAs(item));
}
});
}
James Richardson
time4tea technology
Re: there is just so much wrong with that article & the code in it.
Nov 14, 2007 12:28 PM by Nathan Hughes
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
23 of 26 01/04/2008 14:40
how amazingly clueless of you.
James, your first comment is the most interesting. Can "new" really be thought of as
a dependency injector? The thing about "new" is that whoever is calling it
needs to know the implementation of the interface.
MyInterface foo = new MyImplementation();
It's not always desirable for the caller to have to worry about that. Particularly, w
hen unit testing, and a separate implementation would be desired.
MyInterface foo = new MyImplemenationMock();
To me, the most important advantage of Spring is AOP. One can advise methods of
MyInterface
the
foo
instance.
I think it can. Whoever is calling "new" does indeed need to pick an implementation.
Really all that spring is doing is moving that "new" call out of java (where its
easily refactored) and into xml(where it isn't). Additionally, by using the xml, you
are losing so much information - such as the interface names, the generic types,
and a visual understanding of how it all fits together. By simply calling the object
constructor with the implementations you need in that instance (thereby making
your object fully functional at construction time) you have "injected" it with its de
pendencies. If thats hard, it probably means that the object acts on too many
things, and thus could do with some refactoring. By losing the visual clues, you also
lose important information about how your application _reads_, because you
are too concerned with the xml. Nobody would talk about a FruitBean, and a FruitBeanG
talk about Fruit, Apples and Bananas. How can spring help when unit testing? In a uni
t test you will be testing the functionality of only a single class. Why would
you want an entire framework to get in the way? Writing an xml file, configuring an a
pplicationcontext and pulling the object out of the map cannot possibly be
easier than writing new()...if you need a mock or stub then just make one. Again - if
thats hard because of all the mock setup you need to do, then you probably
need to refactor... If a testcase can't fit into a single screenful of code, mocks or
no, then its an indicator that there's too much going on... AOP is a bit different -
if you need it then fine, but I wouldn't advise it for greenfield applications where
you control the source - just make your code do what you want it to do... James
Richardson
time4tea technology
Hi James, I have read your comments and tend to agree with you. I have seen awful tes
started transactions and executed the real unit test methods. Then, the unit test sta
rter classes loaded these wrapper beans and executed methods on them.
Also with Spring we had to maintain Spring context files for both production and test
ing. Your all Java solution looks much simpler. Are the utility classes that you
used in your example opensource, or would you know an opensource project that has tes
Re: there is just so much wrong with that article & the code in it.
Nov 14, 2007 1:20 PM by Morgan Creighton
new as dependency injection
Nov 14, 2007 5:29 PM by James Richardson
Re: there is just so much wrong with that article & the code in it.
Nov 18, 2007 9:04 AM by Luc Dewavrin
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
24 of 26 01/04/2008 14:40
Is
here
There's really a continuum between unit testing, integration testing, regression test
ing, etc. As the article states, "usually" regression testing is done using
specialized QA tools, often with expensive licenses in proprietary scripting language
s that "programmers" don't have access to, don't easily snap into your
continuous integration server, etc. But this needn't be so. Nowadays we can do more a
nd more of our testing in the JUnit framework, including what we used to
call "system tests" or "regression tests" working the whole system from the end user'
s perspective. I wrote about this below, though I didn't address Spring
specifically:
http://danube.com/blog/michaeljames/junit_is_not_just_for_unit_testing_anymore
"Whoever is calling "new" does indeed need to pick an implementation." ... unless you
are using the Dependency Injection Pattern Martin Fowler does a much
better job of explaining this than I ever could:
http://martinfowler.com/articles/injection.html
making it's main point about testing by using loosely coupled objects by injecting re
sponsibility using inversion of control, a.k.a. the Hollywood principle -- (don't
call me, 'll call you).
Nope - sorry. The point of "dependency injection" is that the object that you are cre
ating doesn't need to know what implementation it will be given, not that the
programmer doesn't need to know! So rather than doing new Implementation() within a c
implementation that it can use, but it doesn't know, or need to know, which one. Henc
e the "dependency" has been "injected". I tried to write a concrete example
here, but was defeated by the editor not allowing XML. I showed a spring example, and
Spring uses bean class="", Java uses new xxx(). In both cases the programmer needs to
different implementations for different circumstances, as can another user of the cla
ss. Eventually somebody needs to pick an implementation though!
Unfortunately both "Dependency Injection" and "Inversion of Control" are terrible nam
es for what is about "Exposing Dependencies". I do totally agree with Martin
Fowler when he says in the article you quoted "My long running default with objects i
s as much as possible, to create valid objects at construction time." James
Richardson
time4tea technology
fully worked example
Nov 18, 2007 5:28 PM by James Richardson
Tear down the wall!
Nov 20, 2007 10:46 PM by Michael James
Re: new as dependency injection
Nov 27, 2007 1:18 PM by Tim Andersen
Re: new as dependency injection
Nov 28, 2007 10:42 AM by James Richardson
Re: new as dependency injection
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
25 of 26 01/04/2008 14:40
new is not dependency injection (DI) DI is made possible by setters, constructors, an
d other means Both primitive Java code and Spring DI container (XML
configuration or Java code to call Spring) can achieve DI. Spring defines conventions
to implement and enforce DI consistently. Spring is not only about DI. In real
world, developers need to define and/or know implementations for interfaces.
InfoQ.com and all content copyright © 2006-2007 C4Media Inc. InfoQ.com hosted at
Feb 24, 2008 9:08 AM by Y Z
InfoQ: Software Testing With Spring Framework
http://www.infoq.com/articles/testing-in-spring
26 of 26 01/04/2008 14:40