Arquillian Constellation
Middleware for Testing (not just Java EE)
Alex Soto

Red Hat Engineer

@alexsotob
@alexsotob2
Alex Soto
Red Hat Engineer
www.lordofthejars.com
@alexsotob
Who Am I?
@alexsotob3
https://www.manning.com/books/testing-java-microservices
@alexsotob4
Questions
@alexsotob5
Integration Tests
@alexsotob6
@alexsotob7
@alexsotob8
@alexsotob9
@alexsotob10
@alexsotob11
@alexsotob12
@alexsotob13
@alexsotob14
Micro-Deployments
@alexsotob15
Create a WAR
Classes to Add
@alexsotob16
Add as Libraries
Maven resolver
Artifact from pom
And its transitivity
@alexsotob17
@alexsotob18
Dependency
@alexsotob19
Setting Application Server
Special Runner
Application Server to Use
@alexsotob20
Auto-Deployment
Take output from package goal
@alexsotob21
DEMO
@alexsotob22
Benefits of Arquillian
> Tests are portable
> Executable
From IDE and Build Tool
> Test mode
in-container, as-client, mixed
> Containers support
Tomcat, Jetty, Payara, Wildfly, TomEE, Weblogic, Websphere, OSGi
> Test Runners
JUnit, TestNG, Cucumber, SerenityBDD, Spock
> Modular, Extensible, Flexible
Lot of integrations (Selenium, JPA, Docker, ..)
@alexsotob23
Containers Are Burning
@alexsotob
Testing Containers
docker build -t myorg/myservice:1.0.0 .
docker run --rm -ti -p 8080:8080 myorg/myservice:1.0.0
docker-compose up
mvn clean test
docker-compose stop
24
Docker Run
Docker Compose Run
Run tests
Stop Docker Containers
@alexsotob25
@alexsotob
Arquillian Cube Example
@RunWith(Arquillian.class)
public class HelloWorldTest {
@ArquillianResource
@DockerUrl(containerName = "helloworld", exposedPort = 8080)
RequestSpecBuilder requestSpecBuilder;
@Test
public void should_receive_ok_message() {
RestAssured
.given()
.spec(requestSpecBuilder.build())
.when()
.get()
.then()
.assertThat().body("status", equalTo("OK"));
}
}
26
Arquillian Runner
REST-Assured Integration
Environment Resolver
Normal REST-Assured Call
helloworld:
image: jonmorehouse/ping-pong
ports:
- "8080:8080"
src/test/docker/docker-compose.yml
@alexsotob
Arquillian Cube DSL
@RunWith(SpringRunner.class)
@SpringBootTest(classes = PingPongController.class, webEnvironment = RANDOM_PORT)
@ContextConfiguration(initializers = PingPongSpringBootTest.Initializer.class)
public class PingPongSpringBootTest {
@ClassRule
public static ContainerDslRule redis = new ContainerDslRule("redis:3.2.6")
.withPortBinding(6379);
@Autowired
TestRestTemplate restTemplate;
@Test
public void should_get_data_from_redis() {
}
27
Spring Boot Test
Custom Initializer
Container Definition
public static class Initializer implements
ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
EnvironmentTestUtils.addEnvironment("testcontainers",
configurableApplicationContext.getEnvironment(),
"spring.redis.host=" + redis.getIpAddress(),
"spring.redis.port=" + redis.getBindPort(6379)
);
Sets Container Environment
@alexsotob28
DEMO
@alexsotob
Arquillian Kube
@RunWith(Arquillian.class)
public class HelloWorldTest {
@ArquillianResource
KubernetesClient client;
@Named(“hello-world-service")
@PortForward
@ArquillianResource
URL url;
@Test
public void testRunningPodStaysUp() throws Exception {
assertThat(client).deployments().pods().isPodReadyForPeriod();
}
}
29
Kubernetes Client
URL to Access Service
AssertJ Custom Assertions
@alexsotob
Serverless
30
Defines function location
@alexsotob
Serverless
31
Defines function location
@alexsotob32
Persistence Tests
@alexsotob
First attempt
@Test
public void should_find_composer_by_name() {
// Given:
clearDatabase(jdbcUri);
insertComposersData(jdbcUri);
ComposersRepository composersRepository = new ComposersRepository();
// When:
Composer mozart = composersRepository.findComposerByName("Wolfgang Amadeus Mozart");
// Then:
assertThat(mozart).returns("Wolfgang Amadeus Mozart", Composer::getName);
}
33
Prepare Database
Execute Query
@alexsotob34
APE
@alexsotob
APE SQL example
@Rule
public ArquillianPersistenceRule arquillianPersistenceRule =
new ArquillianPersistenceRule();
@DbUnit
@ArquillianResource
RdbmsPopulator rdbmsPopulator;
@Before
public void populateData() {
// Given:
rdbmsPopulator.forUri(jdbcUri).withDriver(Driver.class).withUsername("sa")
.withPassword("").usingDataSet("composers.yml")
.execute();
}
35
APE JUnit Rule
(not necessary with
Arquillian Runner)
Set DBUnit usage
Configure Connection and Dataset
Populate
composers:
- id: 1
name: Wolfgang Amadeus Mozart
birthdate: 27/1/1756
died: 5/12/1791
@alexsotob
APE SQL example
@After
public void clean_database() {
// Given:
rdbmsPopulator.forUri(jdbcUri).withDriver(Driver.class).withUsername("sa")
.withPassword("").usingDataSet("composers.yml")
.clean();
}
36
Clean After Test
Clean Database
@alexsotob37
DEMO
@alexsotob38
Benefits of Arquillian APE
> Boilerplate Code
> Programmatic/Declarative
@UsingDataSet/@ShouldMatchDataSet
> SQL Support
DBUnit and Flyway
> REST API Support
Postman Collections
> NoSQL Support
MongoDB, Couchbase, CouchDB, Vault, Redis, Infinispan
@alexsotob39
Contract Tests
@alexsotob40
Consumer
request
response Stub Server
expectations
Provider
request
response
Result
Define Consumer Expectations Verify Expectations On Provider
@alexsotob41
@RunWith(Arquillian.class)
public class CrimesConsumerContractTest {
@StubServer URL pactServer;
@Pact(provider = "crimes", consumer = "villains")
public RequestResponsePact returnListOfCrimes(PactDslWithProvider builder) {
return builder
.uponReceiving("Gru villain to get all Crimes")
.path("/crimes/Gru")
.method("GET")
.willRespondWith()
.status(200)
.body(RESPONSE)
.toPact();
}
@Test
@PactVerification("crimes")
public void should_get_list_of_crimes_by_villain() {
CrimesGateway crimesGateway = new CrimesGateway(webClient, pactServer);
final Single<JsonArray> gruCrimes = crimesGateway.getCrimesByVillainName("Gru");
}
Stub/proxy Server URL
Defines service interaction
Provides predefined Req/Res
Executes real requests
Consumer side
@alexsotob42
@RunWith(Arquillian.class)
@Provider("crimes")
@ContractsFolder("~/crimescontract")
public class CrimesContractTest {
@ArquillianResource
Target target;
@Test
public void should_validate_contract() {
assertThat(target).withUrl(getCrimesServer()).satisfiesContract();
}
}
Provider under validation
Location of contracts
Http Client
Asserts All Interactions are correct
Provider side
@alexsotob43
Benefits of CDC and Pact
> Pact Foundation
Pact specification v3
> Integration with several languages
JVM, Ruby, Python, Go, .Net, Swift, JS
> Pact Broker
Sharing contracts, API documentation, Overview of services
> Arquillian Algeron
Arquillian ecosystem + Pact, Publishers/Retrievers, JBoss Forge
> Consumer Driven Contracts
Fail fast
Independent deployments
Improve communication
@alexsotob44
Visual Testing
@alexsotob
Selenium
45
@alexsotob
Arquillian Drone
46
@alexsotob
Arquillian Drone example
@RunWith(Arquillian.class)
public class LoginScreenTest {
@Drone
private WebDriver browser;
@Test
public void should_login_user() {
driver.get("www.mypage.com/login");
driver.findElement(By.id("loginForm:login")).click();
}
}
47
Arquillian Runner
Injection of WebDriver instance
@alexsotob
Arquillian Graphene
48
@alexsotob
Arquillian Graphene Page Object example
@Location("login.xhtml")
public class HomePage {
@Page
private UserPage userPage;
@FindBy(id = "submit")
private WebElement submitButton;
@FindBy(id = "form")
private LoginPopup loginPopup;
// methods
}
49
Location of the page
Another Page Object
Find web element by id
Page fragment
@alexsotob
Arquillian Graphene example
@RunWith(Arquillian.class)
public class LoginTest {
@Drone
WebDriver webDriver;
@Test
public void should_create_a_new_speaker(@InitialPage HomePage homePage) {
homePage.login("username", "pasword");
}
}
50
Open given page
Use Page Object method
@alexsotob51
Docker and Selenium
@alexsotob52
DEMO
@alexsotob53
Benefits of Graphene
> Reduces Configuration Code
> Page Object pattern
With extra features like AJAX-enabled, fragments, JQuery expressions
> Docker integration
Multiple versions, multiple browsers, recording
@alexsotob54
Speeding Test Execution
@alexsotob55
Production Sources Tests
https://martinfowler.com/articles/rise-test-impact-analysis.html
@alexsotob
Smart Testing Maven Extension
curl -sSL https://git.io/v5jy6 | bash
56
Installs extension
mvn clean test -Dsmart.testing="new, changed, affected"
Runs only new, changed and prod related tests
mvn clean test -Dsmart.testing.mode=ordering -Dsmart.testing="new, changed, affected”
Prioritize new, changed and prod related tests
@alexsotob57
DEMO
@alexsotob58
More about Smart Testing
> Heuristics
new, changed, affected, failed and categorized
> Range of Commits
Define where you want to look at
> Configuration
System Properties or YAML file (global or per module)
> Jenkins Pipeline Shared Library
Integrate with your CI/CD pipeline
> Detection of none class changes
@WatchFile("src/main/resources/META-INF/
> Surefire based
Works with any kind of test that can be run in Surefire
@alexsotob59
Regression-free versions
@alexsotob60
Diferencia
@alexsotob61
Simple Comparison
@alexsotob62
Noise Detection Comparison
@alexsotob63
DEMO
@alexsotob64
More about Diferencia
> Comparison
with, without noise detection, strict, subset
> Formats supported
JSON, Plain Text (automatic detection), HTTPS
> Monitoring
HTTP Status, Prometheus
> Rest API
Configure, Stats
> Docker Image
Ready to use in Kubernetes and OpenShift
https://developers.redhat.com/
@alexsotob
asotobue@redhat.com

Arquillian Constellation