Effective out-of-container Integration Testing
SamBrannenSwi+mindGmbH
SpeakerProfile
• SeniorSo+wareConsultant–Swi+mindGmbH• Javadeveloperwith13+years'experience• SpringFrameworkCoreDeveloper– AuthoroftheSpringTestContextFramework
• PreviousSpringSourcedmServer™developer• RegularspeakeratconferencesonSpring,dmServer,Java,OSGi,andtesOng
• LeadauthorofSpringinaNutshell;chieftechnicalreviewerforSpringRecipes
Agenda
• Background• GoalsofIntegraOonTesOng• TheChallenge• ProposedSoluOon• DIandIoCasEnablers• SimulaOngaProducOonEnvironment• SpringTestContextFramework• Q&A
Background
TypesofTests
• unittests• integraOontests– componentinteracOon:mulOpleunits
– cross‐processinteracOon:database,mail,etc.
• systemtests– end‐to‐endtesOngwithlivesystems
• useracceptancetests
UnitTests
• aresimpletosetup• usedynamicmocksorstubsfordependencies
• instanOatetheSUTandtestit• runfast• butonlytestasingleunit
IntegraOonTests
• testinteracOonsbetweenmulOplecomponents
• relaOvelyeasytosetupwithoutexternalsystemdependencies
• challengingtosetupwithexternalsystemdependencies
• morechallengingwhen/ifapplicaOoncodedependsonthecontainer
ModernEnterpriseJavaApplicaOons
• Integratewithexternalsystems– SMTP,FTP,LDAP,RDBMS,WebServices,JMS
• Relyoncontainer‐providedfuncOonality– datasources,connecOonfactories,transacOonmanager
GoalsofIntegra3onTes3ng
EffecOveIntegraOonTesOng
• Fast• Repeatable• Automated
• Easytoconfigure• Providegoodcodecoverageofend‐to‐endbusinessusecases
Out‐of‐container
• Zerorelianceonavailabilityofexternalsystems
• Canberunanywhere– developerworkstaOon– CIserver
• ApproximateatargetproducOonenvironment
TheChallenge
Howcanweeffec9velyintegraOontestanenterpriseJavaapplicaOon...
outsidethecontainer...
whilesOllge_ngascloseaspossibletoaproducOon‐likeenvironment?
ProposedSolu3on
ApproximatetheproducOonenvironmentby...
• Usingopensourcelibrariesandframeworkstosimulate– asingleexternalsystemdependency,or– mulOpleexternalsystemdependencies
• UsingtheSpringTestContextFrameworktodrivefast,out‐of‐containerintegraOontests– withsimulatedexternalsystemsstartedin‐memory
DIandIoCasEnablers
DependencyInjec9onandInversionofControlcollec9velydecoupleapplica9oncodefromthedeploymentenvironment.
DIandIoCIncreaseTestability
• Dependenciesareinjected,notexplicitlyinstanOated– NotonlyforapplicaOoncomponents,butalsoforinfrastructurecomponents
• JNDIlook‐upsforcontainerprovidedresources– EJBs,datasources,connecOonfactories,etc.
• Programmingtointerfaces– AllowsustotransparentlyswapimplementaOons
Simula3ngaProduc3onEnvironment
Step1
• EnsureyourapplicaOonconfiguraOoncanbesplitinto:– applicaOon‐specificconfiguraOon• businesslogic• servicelayer,repositorylayer,etc.
– environment‐specificconfiguraOon• infrastructure
Step2
• Ensureenvironment‐specificcomponentscanbeeasilyoverriddenintestconfiguraOon–forexample,byusingwelldefinedbeanIDs.– DataSource:dataSource– PladormTransacOonManager:transac9onManager
– JMSConnecOonFactory:connec9onFactory
Step3
• Useopensourcereplacementsforenvironment‐specificcomponents– preferablyconfigurableviaSpringApplicaOonContext
– typicallyin‐memory(a.k.a.,embeddedmode)
OpenSourceFrameworksforEmbeddedUse
Database
• Useanin‐memorydatabasesuchas– HSQL,H2,orDerby
• Spring3.0providesexplicitsupportfor– Embeddeddatabases– PopulaOngexisOngdatabases
EmbeddedDatabaseinXML<jdbc:embedded-database id="dataSource" type="H2"> <jdbc:script location="classpath:/my-schema.sql" /> <jdbc:script location="classpath:/my-data.sql" /></jdbc:embedded-database>
Or simply…
<jdbc:embedded-database id="dataSource" />
EmbeddedDatabaseinCodeprivate EmbeddedDatabase db;
@Before public void launchDatabase() { db = new EmbeddedDatabaseBuilder() .addDefaultScripts() .build();}// tests …
@After public void shutdownDatabase() { db.shutdown();}
PopulateDatabaseinXML<jdbc:initialize-database data-source="dataSource"> <jdbc:script location="classpath:/schema_01.sql" /> <jdbc:script location="classpath:/schema_02.sql" /> <jdbc:script location="classpath:/data_01.sql" /> <jdbc:script location="classpath:/data_02.sql" /></jdbc:initialize-database>
JMS
• Useanin‐memorymessagebrokersuchasAcOveMQ
• AcOveMQcanbeeasilyconfiguredinaSpringApplicaOonContext
• Versions>4.1evenprovidean<amq/>XMLnamespaceforDSL‐likeconfiguraOon
AcOveMQSpringConfiguraOon<bean id="connectionFactory" class= "org.apache.activemq.spring.ActiveMQConnectionFactory” p:brokerURL="vm://localhost?broker.persistent=false" />
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate" p:connectionFactory-ref="connectionFactory" />
SMTPServer
• UseDumbster'sSimpleSmtpServer– Configurableportnumber
– Tracksallmailssent– Providesmethodsforretrieving• Numberofmailssent
• Individualmails• Mailheaders
• Mailbody
AbstractEmailSenderTest(1/2)public abstract class AbstractEmailSenderTest {
protected SimpleSmtpServer server;
@Before public void startSmtpServer() { server = SimpleSmtpServer.start(getSmtpPort()); }
@After public void stopSmtpServer() { server.stop(); }
protected abstract int getSmtpPort();
AbstractEmailSenderTest(2/2) protected void assertNumEmailsSent(SimpleSmtpServer server, int expected) { assertEquals("Verifying number of e-mails sent.”, expected, server.getReceivedEmailSize()); }
protected void assertEmailHeaderValue(SmtpMessage email, String header, String expected) { assertEquals(expected, email.getHeaderValue(header)); }
protected void assertEmailBodyContains(SmtpMessage email, String expected) { assertTrue(email.getBody().contains(expected)); }
PlainEmailSenderTest(1/2)@ContextConfiguration("/applicationContext.xml")public class PlainEmailSenderTest extends AbstractEmailSenderTest {
@Autowired private PlainEmailSenderImpl plainEmailSender;
@Override protected int getSmtpPort() { return 62525; }
PlainEmailSenderTest(2/2)@Testpublic void plainEmail() throws Exception { plainEmailSender.sendSimpleEmail(recipientEmail);
assertNumEmailsSent(server, 1);
SmtpMessage email = (SmtpMessage) server.getReceivedEmail().next();
assertEmailHeaderValue(email, "Subject", "testing"); assertEmailHeaderValue(email, "From", "[email protected]"); assertEmailBodyContains(email, "This is a test email");}
FTPServer
• UseMockFtpServer's– FakeFtpServer• Virtualorin‐memoryfilesystem• Useraccountsandpermissions
– StubFtpServer• Low‐levelFTPservercommands
ServletContainer
• UseJenyorTomcatinembeddedmode• ConfigureJenywithMavenandexecutethejeny:rungoal
ConfiguraOonTips
• ExternalizeconnecOonse_ngsinJavaProperOesfiles
• DefinedifferentProperOesfilesforproducOonandtesOng
• UseSpring'sPropertyPlaceholderConfigureror<context:property‐placeholder>forpropertyreplacement
AvoidHostname&PortCollisions
• PayspecialanenOontohostnamesandports– SMTP
– FTP– Servletcontainer
• ConsiderusingsomethinglikeSpring'sFreePortScanner
EmbeddedHSQLdatabasewithSpring’s<jdbc/>namespace
HibernateEventRepositoryTests
Demo
JMSwithanin‐memoryAcOveMQmessagebroker
JmsNamespaceTest
Demo
EmailwithDumbster’sSimpleSmtpServer
PlainEmailSenderTest
Demo
EmbeddedJenyServletcontainer
AbstractJeKyTest
Demo
FurtherResources• HSQLDB
– hnp://hsqldb.org• H2
– hnp://www.h2database.com
• AcOveMQ– hnp://acOvemq.apache.org
• Dumbster– hnp://quintanaso+.com/dumbster
• MockFtpServer– hnp://mock+pserver.sourceforge.net
• Jeny– hnp://jeny.codehaus.org
• Tomcat– hnp://tomcat.apache.org
Q&A
SamBrannen
sam.brannen[at]swi+mind[dot]com
hnp://www.swi+mind.com
hnp://twiner.com/sam_brannen
“SpringinaNutshell” hnp://oreilly.com/catalog/9780596801946 availablefromO’Reillyin2011
Top Related