@nicolas_frankel
INTEGRATION TESTING
FROM THE TRENCHES
Rebooted
@nicolas_frankel
ME, MYSELF AND I
● Developer
● Developer advocate
@nicolas_frankel
European alternative to the “big” cloud-computing
players
● Privacy-minded
● Great support
@nicolas_frankel
FROM THE BOOK
@nicolas_frankel
INTRODUCTION
INTEGRATION TESTING FROM THE TRENCHES - REBOOTED
@nicolas_frankel
TESTING?
● Unit Testing
● Mutation Testing
● GUI Testing
● Performance Testing
○ Load Testing
○ Stress Testing
● Endurance Testing
● Security Testing
● etc.
@nicolas_frankel
UNIT VS. INTEGRATION TESTING
● Unit Testing
○ Testing a unit in isolation
● Integration Testing
○ Testing the collaboration
of multiple units
@nicolas_frankel
EXAMPLE
● A prototype car
@nicolas_frankel
UNIT TESTING
● Akin to testing each nut
and bolt separately
@nicolas_frankel
INTEGRATION TESTING
● Akin to going on a test
drive
@nicolas_frankel
UNIT + INTEGRATION TESTING
● Take a prototype car on a test
drive having tested only nuts and
bolts?
● Manufacture a car having tested
nuts and bolts but without having
tested it on numerous test
drives?
@nicolas_frankel
SYSTEM UNDER TEST
● SUT is what get tested
● Techniques from Unit
Testing can be re-used
○ Dependency Injection
○ Test doubles
@nicolas_frankel
REMINDER: TEST DOUBLES
● Dummy
● Mock
● Stub
● Spy
● Fake
@nicolas_frankel
TESTING IS ABOUT ROI
● The larger the SUT
○ The more fragile the
test
○ The less maintainable
the test
○ The less the ROI
@nicolas_frankel
TESTING IS ABOUT ROI
● Tests have to be organized
in a certain way
○ The bigger the SUT
○ The less the number of
tests
@nicolas_frankel
TESTING IS ABOUT ROI
● Integration Testing
○ Test nominal cases
@nicolas_frankel
INTEGRATION
TESTING
CHALLENGES
INTEGRATION TESTING FROM THE TRENCHES - REBOOTED
@nicolas_frankel
INTEGRATION TESTS CHALLENGES
● Slow
● Fragile
● Hard to diagnose
https://leanpub.com/integrationtest/
@nicolas_frankel
Y U SLOW?
● Uses infrastructure
resources
● Uses container
@nicolas_frankel
COPING WITH SLOWNESS
➢Separate Integration Tests
from Unit Tests
@nicolas_frankel
INTEGRATION TESTS ARE STILL SLOW
● Separating IT doesn’t make
them faster
● But errors can be
uncovered faster
○ Fail fast
○ It will speed testing
@nicolas_frankel
HOW TO SEPARATE?
● Depends on the build tool
○ Ant
○ Maven
○ Gradle
○ Something else?
@nicolas_frankel
REMINDER: MAVEN LIFECYCLE
@nicolas_frankel
MAVEN SUREFIRE PLUGIN
● Bound to the test phase
● Runs by default
○ *Test
○ Test*
○ *TestCase
@nicolas_frankel
MAVEN FAILSAFE PLUGIN
● “Copy” of Surefire
● Different defaults
○ *IT
○ IT*
○ *ITCase
@nicolas_frankel
MAVEN FAILSAFE PLUGIN
● One goal per lifecycle phase
○ pre-integration-test
○ integration-test
○ post-integration-test
○ verify
● Must be bound explicitly
@nicolas_frankel
POM SNIPPET
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>2.17</version>
<executions>
<execution>
<id>integration-test</id>
<goals>
<goal>integration-test</goal>
</goals>
<phase>integration-test</phase>
</execution>
<execution>
<id>verify</id>
<goals>
<goal>verify</goal>
</goals>
<phase>verify</phase>
</execution>
</executions>
</plugin>
@nicolas_frankel
CONTINUOUS INTEGRATION
● Unit Tests run at each commit
● Integration Tests run
“regularly”
○ Daily
○ Hourly
○ Depends on the context
@nicolas_frankel
Y U FRAGILE?
● Usage of infrastructure
resources
○ External
○ Multiple
@nicolas_frankel
INFRASTRUCTURE RESOURCES
● File system
● Time
● Databases
● Web Services
● Mail servers
● FTP Servers
● etc.
@nicolas_frankel
TIME/FILE SYSTEM:
COPING WITH FRAGILITY
● Use Dependency Injection
● Use abstractions
○ e.g. java.nio.file.Path
instead of
java.util.File
@nicolas_frankel
DATABASES: COPING WITH FRAGILITY
● Test the Service layer
○ Mock the repository
● Test the Repository layer
○ Mock the database???
@nicolas_frankel
DATABASES: COPING WITH FRAGILITY
● Use Fakes under your
control
@nicolas_frankel
ORACLE DATABASE USE-CASE
● Use an in-memory data source
and hope for the best
● Use Oracle Express and hope for
the best
● Use a dedicated remote schema
for each developer
○ And your DBAs will hate you
@nicolas_frankel
TRADE-OFF
● The closer to the “real”
infrastructure,
the more complex the
setup
@nicolas_frankel
MANAGING THE GAP RISK
● In-memory databases are
easy to setup
● h2 is such a database
○ Compatibility modes for
most widespread DB
○ jdbc:h2:mem:test;
MODE=Oracle
@nicolas_frankel
WEB SERVICES: COPING WITH FRAGILITY
● Web Services as infrastructure
resources
○ Hosted on-site
○ Or outside
● Different architectures
○ REST(ful)
○ SOAP
@nicolas_frankel
WEB SERVICES: COPING WITH FRAGILITY
● Use Fakes under your
control
@nicolas_frankel
FAKE RESTFUL WEB SERVICES
● Spark
○ Micro web framework
○ A la Sinatra
○ Very few lines of code
○ Just serve static JSON
files
@nicolas_frankel
SPARK SAMPLE
import static spark.Spark.*;
import spark.*;
public class SparkSample{
public static void main(String[] args) {
setPort(5678);
get("/hello", (request, response) -> {
return "Hello World!";
});
get("/users/:name", (request, response) -> {
return "User: " + request.params(":name");
});
get("/private", (request, response) -> {
response.status(401);
return "Go Away!!!";
});
}
}
@nicolas_frankel
FAKE SOAP WEB SERVICES
● Possible with Spark
○ But not efficient
○ Remember about ROI!
@nicolas_frankel
SMARTBEAR SOAPUI
@nicolas_frankel
SMARTBEAR SOAPUI
● Has a GUI
● Good documentation
● Understands
○ Authentication
○ Headers
○ etc.
@nicolas_frankel
SOAPUI USAGE
● Get WSDL
○ Either online
○ Or from a file
● Create MockService
○ Craft the adequate response
● Run the service
● Point the dependency to
localhost
@nicolas_frankel
CHALLENGES TO THE PREVIOUS SCENARIO
● Craft the adequate response?
○ More likely get one from the
real WS
○ And tweak it
● Running in an automated way
○ Save the project
○ Get the SOAPUI jar
○ Read the project and launch
@nicolas_frankel
SOAPUI API
WsdlProject project = new WsdlProject();
String wsdlFile = "file:src/test/resources/ip2geo.wsdl";
WsdlInterface wsdlInterface =
importWsdl(project, wsdlFile, true)[0];
WsdlMockService fakeService =
project.addNewMockService("fakeService");
WsdlOperation wsdlOp =
wsdlInterface.getOperationByName("ResolveIP");
MockOperation fakeOp =
fakeService.addNewMockOperation(wsdlOp);
MockResponse fakeResponse =
fakeOp.addNewMockResponse("fakeResponse");
fakeResponse.setResponseContent(
"<soapenv:Envelope ...</soapenv:Envelope>");
runner = fakeService.start();
@nicolas_frankel
FAKING WEB SERVICE IN REAL-LIFE
● Use the same rules as for UT
○ Keep validation simple
○ Test one thing
● Keep setup simple
○ Don’t put complex logic
○ OK to duplicate setup in each
test
■ Up to a point
Author:I,rolfB
@nicolas_frankel
IN-CONTAINER
TESTING
INTEGRATION TESTING FROM THE TRENCHES - REBOOTED
@nicolas_frankel
INTEGRATION TESTING
● Collaboration between
classes
● Boundaries with external
dependencies
● What did we forget?
@nicolas_frankel
DEPENDENCIES
● The most important
dependency is the
container!
○ Spring
○ Java EE
■ Application server
@nicolas_frankel
SPRING CONFIGURATION TESTING
● So far, we can test:
○ Beans whose dependencies
can be mocked
○ Beans that depend on fake
resources
● What about the configuration?
@nicolas_frankel
DESIGNING TESTABLE CONFIGURATION
● Configuration cannot be
monolithic
○ Break down into fragments
○ Each fragment contains a set
of either
■ Real beans
■ Fake beans
@nicolas_frankel
EXAMPLE: DATASOURCE CONFIGURATION
● Production
JNDI fragment
● Test in-
memory
fragment
@nicolas_frankel
EXAMPLE: DATASOURCE CONFIGURATION
@Bean
public DataSource ds() {
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
lookup.setResourceRef(true);
return lookup.getDataSource("jdbc/MyDS");
}
@Bean
public DataSource ds() {
org.apache.tomcat.jdbc.pool.DataSource ds
= new org.apache.tomcat.jdbc.pool.DataSource();
ds.setDriverClassName("org.h2.Driver");
ds.setUrl("jdbc:h2:~/test");
ds.setUsername("sa");
ds.setMaxActive(1);
return ds;
}
@nicolas_frankel
FRAGMENT STRUCTURE
● Main fragment
○ Repository
○ Service
○ etc.
● Prod DB fragment
● Test DB fragment
@nicolas_frankel
TIPS
● Prevent coupling
○ Use top-level assembly instead
of fragment references
● Pool exhaustion check
○ Set the maximum number of
connections in the pool to 1
● Compile-time safety
○ Use JavaConfig
@nicolas_frankel
NOW, HOW TO TEST?
● Spring Test
○ Integration with
widespread testing
frameworks
@nicolas_frankel
SAMPLE TESTNG TEST WITH SPRING
@ContextConfiguration(
classes = { MainConfig.class, TestDataSourceConfig.class }
)
@RunWith(SpringJUnit4ClassRunner.class)
public class OrderIT {
@Autowired
private OrderService orderService;
@Test
public void should_do_this_and_that() {
orderService.order();
Assert.assertThat(...)
}
}
@nicolas_frankel
TESTING WITH THE DATABASE
● Transactions
○ Bound to business
feature
○ Implemented on Service
layer
○ @Transactional
@nicolas_frankel
TRANSACTION MANAGEMENT TIP
● When tests fail
○ How to audit state?
○ Spring rollbacks
transactions
● @Commit
@nicolas_frankel
TRANSACTION MANAGEMENT
SAMPLE
@ContextConfiguration
@Transactional
@Commit
@RunWith(SpringJUnit4ClassRunner.class)
public class OverrideDefaultRollbackSpringTest{
@Test
@Rollback
public void transaction_will_be_rollbacked() {
...
}
@Test
public void transaction_will_be_committed() {
...
}
}
@nicolas_frankel
END-TO-END TESTING?
● Testing from the UI layer is
fragile
○ UI changes a lot
● URL not so much
@nicolas_frankel
MOCKMVC
● “Main entry point for
server-side Spring MVC test
support”
@nicolas_frankel
MOCKMVC SAMPLE
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("welcome"));
@nicolas_frankel
SPRING BOOT
@nicolas_frankel
SPRING BOOT
● @AutoConfigureMock
Mvc
● Inject MockMvc
@nicolas_frankel
SPRING BOOT
● @SpringBootTest
instead of
@ApplicationContext
@nicolas_frankel
SPRING BOOT LAYERED TESTS
● JPA
○ @DataJpaTest
● Web
○ @WebMvcTest
○ @MockBean
@nicolas_frankel
THE JAVA EE WORLD
● Java EE has unique
challenges
○ CDI has no explicit
wiring
○ Different application
servers
@nicolas_frankel
DEPLOY ONLY WHAT YOU WANT
● Standalone API to deploy only
resources relevant to the test
○ Just pick and choose
● Maven Integration
○ Gradle too…
@nicolas_frankel
SHRINKWRAP SAMPLE
String srcMainWebapp = "src/main/webapp/";
ShrinkWrap.create(WebArchive.class, "myWar.war")
.addClass(MyService.class)
.addPackage(MyModel.class.getPackage())
.addAsWebInfResource("persistence.xml",
"classes/META-INF/persistence.xml")
.addAsWebInfResource(
new File(srcMainWebapp, "WEB-INF/page/my.jsp"),
"page/my.jsp")
.addAsWebResource(
new File(srcMainWebapp, "script/my.js"),
"script/my.js")
.setWebXML("web.xml");
@nicolas_frankel
MAVEN INTEGRATION SAMPLE
File[] libs = Maven.resolver()
.loadPomFromFile("pom.xml")
.importDependencies(COMPILE, RUNTIME)
.resolve()
.withTransitivity().asFile();
ShrinkWrap.create(WebArchive.class,
"myWar.war")
.addAsLibraries(libs);
@nicolas_frankel
DIFFERENT APPLICATION SERVERS
● Abstraction layer to
○ Download
○ Deploy applications
○ Test
● Container adapters
○ TomEE
○ JBoss
○ Weld
○ etc.
● Full Maven integration
@nicolas_frankel
ARQUILLIAN TEST SAMPLE
public class ArquillianSampleIT extends Arquillian {
@Inject
private MyService myService;
@Deployment
public static JavaArchive createDeployment() {
return ...;
}
@Test
public void should_handle_service() {
Object value = myService.handle();
Assert.assertThat(...);
}
}
@nicolas_frankel
ARQUILLIAN CONFIGURATION SAMPLE
<arquillian xmlns="http://jboss.org/schema/arquillian"
xmlns:xsi="..."
xsi:schemaLocation="
http://jboss.org/schema/arquillian
http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
<container qualifier="tomee" default="true">
<configuration>
<property name="httpPort">-1</property>
<property name="stopPort">-1</property>
</configuration>
</arquillian>
@nicolas_frankel
ARQUILLIAN EXTENSIONS
● Persistence: DBUnit
● Graphene: Selenium
● Cube: Docker
● Etc.
@nicolas_frankel
Q&A
http://blog.frankel.ch/
@nicolas_frankel
https://bit.ly/2yJvd1Z

GeeCon.cz - Integration Testing from the Trenches Rebooted