TestContainers

for Fun and Profit

Hello,


We are Estefania Castro

and Juan Medina

Engineers at Santander Technology



 Fany_CV
 JuanMedinaCode

Integration Tests

The point of integration testing, as the name suggests, is to test whether many separately developed modules work together as expected.

Tests

Should be Reliable

and give Certainty

Mocks

They could leak

implementation details

Leaking Mock Example

class CustomerService {
	....
	public boolean saveOrUpdateCustomer(final Customer customer) {
		final Optional<Customer> result = customerRepository.findById(customer.getId());

		if (result.isPresent())) {
			customerRepository.update(customer);
			return true;
		} else {
			customerRepository.save(customer);
			return false;
		}
	}
}
						

class CustomerServiceTest {
	@Test
	public void testSaveOrUpdateCustomer() {
		final Customer testCustomer = new Customer(1, "John", "Doe");

		final customerRepositoryMock = mock(CustomerRepository.class);
		given(customerRepositoryMock.findCustomerById(testCustomer.getId()))
			.willReturn(Optional.empty());
		given(customerRepositoryMock.update(testCustomer))
			.doNothing();

		final CustomerService service = new CustomerService(customerRepositoryMock);
		assertThat(service.saveOrUpdateCustomer(customer)).equalsTo(true);
	}
}
						

Fakes

They are as good

as they are reliable

H2 in memory database vs PostgreSQL


  • Does not support XML queries
  • No Windows Functions
  • No JSON types
  • No TO_TIMESTAMP

TestContainers

Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.

Some of Databases supported


  • DB2
  • Oracle
  • PostgresSQL
  • MongoDB
  • InfluxDB
  • Neo4J
  • Cassandra
  • MySQL & MariaDB
  • Couchbase
  • Elasticsearch

And many other Modules


  • Kafka
  • RabbitMQ
  • Solr
  • Apache Pulsar
  • Hashicorp Vault
  • Webdriver
  • Nginx
  • JDBC
  • R2DBC
  • ToxiProxy

Additionally

You could create any

custom docker for your tests

Demo Time

Let's have fun

doing an example


 https://github.com/LearningByExample/testcontainers-for-fun-and-profit

Profit

Let's think on

future usages

TestContainers is supported


  • Travis
  • CircleCI
  • Github Actions
  • GitLab Pipelines
  • Jenkins

Growing Ecosystem


  • Pivotal spring-cloud-aws using localstack
  • Lighbend and Alpakka Kafka
  • Transferwise and RDBMS tests
  • Apache Camel using consul & etcd
  • Infinispan using LDAP & KeyCloack
  • eBay Marketing using Redis & Kafka
Using DR Header
 https://github.com/juan-medina/drheader-junit-test-containers

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class DrTests {
    @BeforeEach
    void setup() {
      Testcontainers.exposeHostPorts(port);
      drHEADerContainer = new GenericContainer(
        new ImageFromDockerfile("drheader", true)
          .withDockerfileFromBuilder(builder ->
          builder.from("python:3.7.4")
            .run("git clone https://github.com/Santandersecurityresearch/DrHeader.git")
            .run("cd DrHeader && git checkout master && git pull && pip install .")
            .entryPoint("tail -f /dev/null")
            .build()));
      drHEADerContainer.start();
    }

    @AfterEach
    void tearDown() {
       drHEADerContainer.stop();
    }	
}

						


private void drHEADer(final String url) throws Exception {
  final String testUrl = String.format(TEST_CONTAINERS_INTERNAL_URL, port, url);
  final ExecResult execResult = drHEADerContainer.
    execInContainer("drheader", "scan", "single", testUrl);
  
  if (execResult.getExitCode() != 0) {
    throw new AssertionError(String.format("Error on drHEADer analysis : \n%s", 
     execResult.getStdout()));
  }
}

@Test
@DisplayName("Index should not have drHEADer errors")
void IndexShouldNotHaveDrHEADerErrors() throws Exception {
	drHEADer("/index.html");
}
						
Using TestContainers with Resilience4j to test failures
 https://github.com/LearningByExample/testing-resilience

@Test
void whenShouldRecover() throws Exception {
	stopDatabase();
	assertServiceIsReady(false);

	startDatabase();
	loadInitData();
	assertServiceIsReady(true);
	assertOffersAre(INITIAL_OFFERS);

	stopDatabase();
	assertServiceIsReady(true);
	assertOffersAre(INITIAL_OFFERS);

	startDatabase();
	loadInitData();
	addVanillaCookies();
	assertServiceIsReady(true);
	assertOffersAre(ADDITIONAL_OFFERS);
}
																 
							

Thank You

now Questions

and Answers


 https://learningbyexample.github.io/testcontainers-for-fun-and-profit/