├── disque-job-queue
├── src
│ ├── main
│ │ └── java
│ │ │ ├── MailApiClient.java
│ │ │ ├── MailMessage.java
│ │ │ ├── MailSenderService.java
│ │ │ └── DisqueBackedMailSenderService.java
│ └── test
│ │ ├── resources
│ │ └── logback-test.xml
│ │ └── java
│ │ ├── SingleDisqueInstanceTest.java
│ │ └── DisqueBackedMailSenderTest.java
└── pom.xml
├── spring-boot
├── src
│ ├── test
│ │ ├── resources
│ │ │ └── logback-test.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ ├── DemoControllerTest.java
│ │ │ └── AbstractIntegrationTest.java
│ └── main
│ │ └── java
│ │ └── com
│ │ └── example
│ │ ├── DemoApplication.java
│ │ └── DemoController.java
└── pom.xml
├── circle.yml
├── linked-container
├── src
│ ├── test
│ │ ├── resources
│ │ │ └── logback-test.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── linkedcontainer
│ │ │ ├── RedmineContainer.java
│ │ │ └── RedmineClientTest.java
│ └── main
│ │ └── java
│ │ └── com
│ │ └── example
│ │ └── linkedcontainer
│ │ └── RedmineClient.java
└── pom.xml
├── redis-backed-cache
├── src
│ ├── test
│ │ ├── resources
│ │ │ └── logback-test.xml
│ │ └── java
│ │ │ └── RedisBackedCacheTest.java
│ └── main
│ │ └── java
│ │ └── com
│ │ └── mycompany
│ │ └── cache
│ │ ├── Cache.java
│ │ └── RedisBackedCache.java
└── pom.xml
├── selenium-container
├── src
│ └── test
│ │ ├── resources
│ │ └── logback-test.xml
│ │ └── java
│ │ └── SeleniumContainerTest.java
└── pom.xml
├── README.md
├── .gitignore
└── pom.xml
/disque-job-queue/src/main/java/MailApiClient.java:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by rnorth on 03/01/2016.
3 | */
4 | public interface MailApiClient {
5 |
6 | boolean send(MailMessage message) throws MailSendException;
7 |
8 | class MailSendException extends RuntimeException {
9 |
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/spring-boot/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/disque-job-queue/src/main/java/MailMessage.java:
--------------------------------------------------------------------------------
1 | import lombok.AllArgsConstructor;
2 | import lombok.Data;
3 |
4 | /**
5 | * Created by rnorth on 03/01/2016.
6 | */
7 | @Data @AllArgsConstructor
8 | public class MailMessage {
9 | public final String subject;
10 | public final String recipient;
11 | public final String body;
12 | }
13 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | machine:
2 | java:
3 | version: oraclejdk8
4 | environment:
5 | DOCKER_HOST: tcp://127.0.0.1:2376
6 | DOCKER_TLS_VERIFY: 0
7 | MAVEN_OPTS: -Xmx512m
8 | dependencies:
9 | override:
10 | - "sudo docker -d -e lxc -s btrfs -H 0.0.0.0:2376 -H unix:///var/run/docker.sock --log-level=debug --debug=true":
11 | background: true
12 | - mvn install -DskipTests
--------------------------------------------------------------------------------
/spring-boot/src/main/java/com/example/DemoApplication.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | @SpringBootApplication
7 | public class DemoApplication {
8 |
9 | public static void main(String[] args) {
10 | SpringApplication.run(DemoApplication.class, args);
11 | }
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/linked-container/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/disque-job-queue/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/redis-backed-cache/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/selenium-container/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
6 |
7 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/spring-boot/src/main/java/com/example/DemoController.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import org.springframework.data.redis.core.StringRedisTemplate;
4 | import org.springframework.web.bind.annotation.*;
5 |
6 | @RestController
7 | public class DemoController {
8 |
9 | private final StringRedisTemplate stringRedisTemplate;
10 |
11 | public DemoController(StringRedisTemplate stringRedisTemplate){
12 | this.stringRedisTemplate = stringRedisTemplate;
13 | }
14 |
15 | @GetMapping("/foo")
16 | public String get() {
17 | return stringRedisTemplate.opsForValue().get("foo");
18 | }
19 |
20 | @PutMapping("/foo")
21 | public void set(@RequestBody String value) {
22 | stringRedisTemplate.opsForValue().set("foo", value);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/spring-boot/src/test/java/com/example/DemoControllerTest.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import static org.rnorth.visibleassertions.VisibleAssertions.*;
4 |
5 | import org.junit.Test;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.test.web.client.TestRestTemplate;
8 |
9 | public class DemoControllerTest extends AbstractIntegrationTest {
10 |
11 | @Autowired
12 | TestRestTemplate restTemplate;
13 |
14 | @Test
15 | public void simpleTest() {
16 | String fooResource = "/foo";
17 |
18 | info("putting 'bar' to " + fooResource);
19 | restTemplate.put(fooResource, "bar");
20 |
21 | assertEquals("value is set", "bar", restTemplate.getForObject(fooResource, String.class));
22 | }
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/linked-container/src/main/java/com/example/linkedcontainer/RedmineClient.java:
--------------------------------------------------------------------------------
1 | package com.example.linkedcontainer;
2 |
3 | import okhttp3.OkHttpClient;
4 | import okhttp3.Request;
5 | import okhttp3.Response;
6 | import org.json.JSONObject;
7 |
8 | import java.io.IOException;
9 |
10 | /**
11 | * A crude, partially implemented Redmine client.
12 | */
13 | public class RedmineClient {
14 |
15 | private String url;
16 | private OkHttpClient client;
17 |
18 | public RedmineClient(String url) {
19 | this.url = url;
20 | client = new OkHttpClient();
21 | }
22 |
23 | public int getIssueCount() throws IOException {
24 | Request request = new Request.Builder()
25 | .url(url + "/issues.json")
26 | .build();
27 |
28 | Response response = client.newCall(request).execute();
29 | JSONObject jsonObject = new JSONObject(response.body().string());
30 | return jsonObject.getInt("total_count");
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # testcontainers-java-examples
2 |
3 | # ARCHIVED! Please see [examples in the main testcontainers-java repository](https://github.com/testcontainers/testcontainers-java/tree/master/examples)
4 |
5 | > Practical examples of using Testcontainers.
6 |
7 | The code in this repository accompanies the following blog posts:
8 |
9 | * [JUnit integration testing with Docker and Testcontainers](https://rnorth.org/junit-integration-testing-with-docker-and-testcontainers)
10 | * [Fun with Disque, Java and Spinach](https://rnorth.org/fun-with-disque-java-and-spinach)
11 | * [Better JUnit Selenium testing with Docker and Testcontainers](https://rnorth.org/better-junit-selenium-testing-with-docker-and-testcontainers)
12 |
13 | Note that this examples project uses JitPack to obtain SNAPSHOT versions of the latest build of TestContainers. For
14 | normal usage, refer to the [documentation](http://testcontainers.viewdocs.io/testcontainers-java/#user-content-maven-dependencies)
15 | for setting the correct maven dependencies for your project.
16 |
--------------------------------------------------------------------------------
/redis-backed-cache/src/main/java/com/mycompany/cache/Cache.java:
--------------------------------------------------------------------------------
1 | package com.mycompany.cache;
2 |
3 | import java.util.Optional;
4 |
5 | /**
6 | * Cache, for storing data associated with keys.
7 | */
8 | public interface Cache {
9 |
10 | /**
11 | * Store a value object in the cache with no specific expiry time. The object may be evicted by the cache any time,
12 | * if necessary.
13 | *
14 | * @param key key that may be used to retrieve the object in the future
15 | * @param value the value object to be stored
16 | */
17 | void put(String key, Object value);
18 |
19 | /**
20 | * Retrieve a value object from the cache.
21 | * @param key the key that was used to insert the object initially
22 | * @param expectedClass for convenience, a class that the object should be cast to before being returned
23 | * @param the class of the returned object
24 | * @return the object if it was in the cache, or an empty Optional if not found.
25 | */
26 | Optional get(String key, Class expectedClass);
27 | }
--------------------------------------------------------------------------------
/linked-container/src/test/java/com/example/linkedcontainer/RedmineContainer.java:
--------------------------------------------------------------------------------
1 | package com.example.linkedcontainer;
2 |
3 | import org.testcontainers.containers.GenericContainer;
4 | import org.testcontainers.containers.traits.LinkableContainer;
5 | import org.testcontainers.containers.wait.Wait;
6 |
7 | /**
8 | * A Redmine container.
9 | */
10 | public class RedmineContainer extends GenericContainer {
11 |
12 | private static final int REDMINE_PORT = 3000;
13 |
14 | public RedmineContainer(String dockerImageName) {
15 | super(dockerImageName);
16 | }
17 |
18 | @Override
19 | protected void configure() {
20 | addExposedPort(REDMINE_PORT);
21 | waitingFor(Wait.forHttp("/"));
22 | }
23 |
24 | public RedmineContainer withLinkToContainer(LinkableContainer otherContainer, String alias) {
25 | addLink(otherContainer, alias);
26 | return this;
27 | }
28 |
29 | public String getRedmineUrl() {
30 | return String.format("http://%s:%d",
31 | this.getContainerIpAddress(),
32 | this.getMappedPort(REDMINE_PORT));
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/redis-backed-cache/src/main/java/com/mycompany/cache/RedisBackedCache.java:
--------------------------------------------------------------------------------
1 | package com.mycompany.cache;
2 |
3 | import com.google.gson.Gson;
4 | import redis.clients.jedis.Jedis;
5 |
6 | import java.util.Optional;
7 |
8 | /**
9 | * An implementation of {@link Cache} that stores data in Redis.
10 | */
11 | public class RedisBackedCache implements Cache {
12 |
13 | private final Jedis jedis;
14 | private final String cacheName;
15 | private final Gson gson;
16 |
17 | public RedisBackedCache(Jedis jedis, String cacheName) {
18 | this.jedis = jedis;
19 | this.cacheName = cacheName;
20 | this.gson = new Gson();
21 | }
22 |
23 | @Override
24 | public void put(String key, Object value) {
25 | String jsonValue = gson.toJson(value);
26 | this.jedis.hset(this.cacheName, key, jsonValue);
27 | }
28 |
29 | @Override
30 | public Optional get(String key, Class expectedClass) {
31 | String foundJson = this.jedis.hget(this.cacheName, key);
32 |
33 | if (foundJson == null) {
34 | return Optional.empty();
35 | }
36 |
37 | return Optional.of(gson.fromJson(foundJson, expectedClass));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Maven template
3 | target/
4 | pom.xml.tag
5 | pom.xml.releaseBackup
6 | pom.xml.versionsBackup
7 | pom.xml.next
8 | release.properties
9 | dependency-reduced-pom.xml
10 | buildNumber.properties
11 | .mvn/timing.properties
12 | ### JetBrains template
13 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
14 |
15 | *.iml
16 |
17 | ## Directory-based project format:
18 | .idea/
19 | # if you remove the above rule, at least ignore the following:
20 |
21 | # User-specific stuff:
22 | # .idea/workspace.xml
23 | # .idea/tasks.xml
24 | # .idea/dictionaries
25 |
26 | # Sensitive or high-churn files:
27 | # .idea/dataSources.ids
28 | # .idea/dataSources.xml
29 | # .idea/sqlDataSources.xml
30 | # .idea/dynamic.xml
31 | # .idea/uiDesigner.xml
32 |
33 | # Gradle:
34 | # .idea/gradle.xml
35 | # .idea/libraries
36 |
37 | # Mongo Explorer plugin:
38 | # .idea/mongoSettings.xml
39 |
40 | ## File-based project format:
41 | *.ipr
42 | *.iws
43 |
44 | ## Plugin-specific files:
45 |
46 | # IntelliJ
47 | /out/
48 |
49 | # mpeltonen/sbt-idea plugin
50 | .idea_modules/
51 |
52 | # JIRA plugin
53 | atlassian-ide-plugin.xml
54 |
55 | # Crashlytics plugin (for Android Studio and IntelliJ)
56 | com_crashlytics_export_strings.xml
57 | crashlytics.properties
58 | crashlytics-build.properties
59 |
60 | # Eclipse
61 | .project
62 | .classpath
63 | .settings
64 | bin
65 |
66 |
--------------------------------------------------------------------------------
/selenium-container/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | testcontainers-java-examples
7 | org.testcontainers.examples
8 | NOVERSION
9 |
10 | 4.0.0
11 |
12 | selenium-container
13 |
14 |
15 |
16 | org.seleniumhq.selenium
17 | selenium-remote-driver
18 | 2.45.0
19 |
20 |
21 | ${testcontainers.group}
22 | selenium
23 | ${testcontainers.version}
24 |
25 |
26 | org.slf4j
27 | slf4j-api
28 | 1.7.7
29 | provided
30 |
31 |
32 | ch.qos.logback
33 | logback-classic
34 | 1.1.2
35 | test
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/disque-job-queue/src/main/java/MailSenderService.java:
--------------------------------------------------------------------------------
1 | import lombok.Data;
2 | import lombok.RequiredArgsConstructor;
3 |
4 | import java.util.concurrent.Callable;
5 |
6 | /**
7 | * Created by rnorth on 03/01/2016.
8 | */
9 | public interface MailSenderService extends Callable {
10 |
11 | /**
12 | * Enqueue a message to be sent on a subsequent {@link DisqueBackedMailSenderService#doScheduledSend()}
13 | * @param message mail message to be sent
14 | * @return an ID for the enqueued message
15 | */
16 | String enqueueMessage(MailMessage message);
17 |
18 | /**
19 | * Trigger a scheduled send of mail messages.
20 | *
21 | * TODO: In a real implementation this could be called by a {@link java.util.concurrent.ScheduledExecutorService},
22 | * probably
23 | *
24 | * @return the result of sending queued messages.
25 | */
26 | MailSenderService.Result doScheduledSend();
27 |
28 | /**
29 | * call() implementation to allow MailSenderService to be used as a Callable. Simply delegates to
30 | * the {@link DisqueBackedMailSenderService#doScheduledSend()} method.
31 | *
32 | * @return the result of sending queued messages.
33 | * @throws Exception
34 | */
35 | default MailSenderService.Result call() throws Exception {
36 | return doScheduledSend();
37 | }
38 |
39 | @Data
40 | @RequiredArgsConstructor
41 | class Result {
42 | public final int successfulCount;
43 | public final int failedCount;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/linked-container/src/test/java/com/example/linkedcontainer/RedmineClientTest.java:
--------------------------------------------------------------------------------
1 | package com.example.linkedcontainer;
2 |
3 | import org.junit.Rule;
4 | import org.junit.Test;
5 | import org.junit.rules.RuleChain;
6 | import org.testcontainers.containers.PostgreSQLContainer;
7 |
8 | import static org.junit.Assert.assertEquals;
9 |
10 | /**
11 | * Tests for RedmineClient.
12 | */
13 | public class RedmineClientTest {
14 |
15 | private static final String POSTGRES_USERNAME = "redmine";
16 | private static final String POSTGRES_PASSWORD = "secret";
17 |
18 | private PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.2")
19 | .withUsername(POSTGRES_USERNAME)
20 | .withPassword(POSTGRES_PASSWORD);
21 |
22 | private RedmineContainer redmineContainer = new RedmineContainer("redmine:3.3.2")
23 | .withLinkToContainer(postgreSQLContainer, "postgres")
24 | .withEnv("POSTGRES_ENV_POSTGRES_USER", POSTGRES_USERNAME)
25 | .withEnv("POSTGRES_ENV_POSTGRES_PASSWORD", POSTGRES_PASSWORD);
26 |
27 | @Rule
28 | public RuleChain chain = RuleChain.outerRule(postgreSQLContainer)
29 | .around(redmineContainer);
30 |
31 | @Test
32 | public void canGetIssueCount() throws Exception {
33 | RedmineClient redmineClient = new RedmineClient(
34 | redmineContainer.getRedmineUrl());
35 |
36 | assertEquals(
37 | "The issue count can be retrieved.",
38 | 0,
39 | redmineClient.getIssueCount());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/spring-boot/src/test/java/com/example/AbstractIntegrationTest.java:
--------------------------------------------------------------------------------
1 | package com.example;
2 |
3 | import org.junit.ClassRule;
4 | import org.junit.runner.RunWith;
5 | import org.springframework.boot.test.context.SpringBootTest;
6 | import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
7 | import org.springframework.boot.test.util.TestPropertyValues;
8 | import org.springframework.context.ApplicationContextInitializer;
9 | import org.springframework.context.ConfigurableApplicationContext;
10 | import org.springframework.test.context.ContextConfiguration;
11 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 | import org.testcontainers.containers.GenericContainer;
13 |
14 | @RunWith(SpringJUnit4ClassRunner.class)
15 | @SpringBootTest(classes = DemoApplication.class,webEnvironment = WebEnvironment.RANDOM_PORT)
16 | @ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class)
17 | public abstract class AbstractIntegrationTest {
18 |
19 | @ClassRule
20 | public static GenericContainer redis = new GenericContainer("redis:3.0.6").withExposedPorts(6379);
21 |
22 | public static class Initializer implements ApplicationContextInitializer {
23 | @Override
24 | public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
25 | TestPropertyValues values = TestPropertyValues.of(
26 | "spring.redis.host=" + redis.getContainerIpAddress(),
27 | "spring.redis.port=" + redis.getMappedPort(6379)
28 | );
29 | values.applyTo(configurableApplicationContext);
30 | }
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/disque-job-queue/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | testcontainers-java-examples
7 | org.testcontainers.examples
8 | NOVERSION
9 |
10 | 4.0.0
11 |
12 | disque-job-queue
13 |
14 |
15 | biz.paluch.redis
16 | spinach
17 | 0.2
18 |
19 |
20 | com.google.code.gson
21 | gson
22 | 2.5
23 |
24 |
25 | com.google.guava
26 | guava
27 | 18.0
28 |
29 |
30 | org.slf4j
31 | slf4j-api
32 | 1.7.7
33 | provided
34 |
35 |
36 | ch.qos.logback
37 | logback-classic
38 | 1.1.2
39 | test
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/redis-backed-cache/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | testcontainers-java-examples
7 | org.testcontainers.examples
8 | NOVERSION
9 |
10 | 4.0.0
11 |
12 | redis-backed-cache
13 |
14 |
15 | redis.clients
16 | jedis
17 | 2.8.0
18 |
19 |
20 | com.google.code.gson
21 | gson
22 | 2.5
23 |
24 |
25 | com.google.guava
26 | guava
27 | 18.0
28 |
29 |
30 | org.slf4j
31 | slf4j-api
32 | 1.7.7
33 | provided
34 |
35 |
36 | ch.qos.logback
37 | logback-classic
38 | 1.1.2
39 | test
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/selenium-container/src/test/java/SeleniumContainerTest.java:
--------------------------------------------------------------------------------
1 | import org.junit.Rule;
2 | import org.junit.Test;
3 | import org.openqa.selenium.WebElement;
4 | import org.openqa.selenium.remote.DesiredCapabilities;
5 | import org.openqa.selenium.remote.RemoteWebDriver;
6 | import org.testcontainers.containers.BrowserWebDriverContainer;
7 |
8 | import java.io.File;
9 |
10 | import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue;
11 | import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL;
12 |
13 | /**
14 | * Simple example of plain Selenium usage.
15 | */
16 | public class SeleniumContainerTest {
17 |
18 | @Rule
19 | public BrowserWebDriverContainer chrome = new BrowserWebDriverContainer()
20 | .withDesiredCapabilities(DesiredCapabilities.chrome())
21 | .withRecordingMode(RECORD_ALL, new File("target"));
22 |
23 | @Test
24 | public void simplePlainSeleniumTest() {
25 | RemoteWebDriver driver = chrome.getWebDriver();
26 |
27 | driver.get("https://wikipedia.org");
28 | WebElement searchInput = driver.findElementByName("search");
29 |
30 | searchInput.sendKeys("Rick Astley");
31 | searchInput.submit();
32 |
33 | WebElement otherPage = driver.findElementByLinkText("Rickrolling");
34 | otherPage.click();
35 |
36 | boolean expectedTextFound = driver.findElementsByCssSelector("p")
37 | .stream()
38 | .anyMatch(element -> element.getText().contains("meme"));
39 |
40 | assertTrue("The word 'meme' is found on a page about rickrolling", expectedTextFound);
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/redis-backed-cache/src/test/java/RedisBackedCacheTest.java:
--------------------------------------------------------------------------------
1 | import com.mycompany.cache.Cache;
2 | import com.mycompany.cache.RedisBackedCache;
3 | import org.junit.Before;
4 | import org.junit.Rule;
5 | import org.junit.Test;
6 | import org.testcontainers.containers.GenericContainer;
7 | import redis.clients.jedis.Jedis;
8 |
9 | import java.util.Optional;
10 |
11 | import static org.rnorth.visibleassertions.VisibleAssertions.*;
12 |
13 | /**
14 | * Integration test for Redis-backed cache implementation.
15 | */
16 | public class RedisBackedCacheTest {
17 |
18 | @Rule
19 | public GenericContainer redis = new GenericContainer("redis:3.0.6")
20 | .withExposedPorts(6379);
21 | private Cache cache;
22 |
23 | @Before
24 | public void setUp() throws Exception {
25 | Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379));
26 |
27 | cache = new RedisBackedCache(jedis, "test");
28 | }
29 |
30 | @Test
31 | public void testFindingAnInsertedValue() {
32 | cache.put("foo", "FOO");
33 | Optional foundObject = cache.get("foo", String.class);
34 |
35 | assertTrue("When an object in the cache is retrieved, it can be found",
36 | foundObject.isPresent());
37 | assertEquals("When we put a String in to the cache and retrieve it, the value is the same",
38 | "FOO",
39 | foundObject.get());
40 | }
41 |
42 | @Test
43 | public void testNotFindingAValueThatWasNotInserted() {
44 | Optional foundObject = cache.get("bar", String.class);
45 |
46 | assertFalse("When an object that's not in the cache is retrieved, nothing is found",
47 | foundObject.isPresent());
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/linked-container/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | testcontainers-java-examples
7 | org.testcontainers.examples
8 | NOVERSION
9 |
10 | 4.0.0
11 |
12 | linked-container
13 |
14 |
15 |
16 | ${testcontainers.group}
17 | postgresql
18 | ${testcontainers.version}
19 |
20 |
21 | org.postgresql
22 | postgresql
23 | 9.4.1212
24 | test
25 |
26 |
27 | org.slf4j
28 | slf4j-api
29 | 1.7.24
30 | provided
31 |
32 |
33 | ch.qos.logback
34 | logback-classic
35 | 1.2.1
36 | test
37 |
38 |
39 | com.squareup.okhttp3
40 | okhttp
41 | 3.6.0
42 |
43 |
44 | org.json
45 | json
46 | 20160810
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/spring-boot/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 | testcontainers-java-examples
7 | org.testcontainers.examples
8 | NOVERSION
9 |
10 | 4.0.0
11 |
12 | spring-boot
13 |
14 |
15 | UTF-8
16 | 1.8
17 |
18 |
19 |
20 |
21 |
22 | org.springframework.boot
23 | spring-boot-dependencies
24 | 2.0.1.RELEASE
25 | pom
26 | import
27 |
28 |
29 |
30 |
31 |
32 |
33 | org.springframework.boot
34 | spring-boot-starter-data-redis
35 |
36 |
37 | org.springframework.boot
38 | spring-boot-starter-web
39 |
40 |
41 |
42 | org.springframework.boot
43 | spring-boot-starter-test
44 | test
45 |
46 |
47 |
48 |
49 |
50 |
51 | org.springframework.boot
52 | spring-boot-maven-plugin
53 | 2.0.1.RELEASE
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/disque-job-queue/src/main/java/DisqueBackedMailSenderService.java:
--------------------------------------------------------------------------------
1 | import biz.paluch.spinach.DisqueClient;
2 | import biz.paluch.spinach.api.Job;
3 | import biz.paluch.spinach.api.sync.DisqueCommands;
4 | import com.google.gson.Gson;
5 |
6 | import java.util.HashSet;
7 | import java.util.List;
8 | import java.util.Set;
9 | import java.util.concurrent.TimeUnit;
10 |
11 | /**
12 | * Simple example of how a mail service can be backed by a Disque job queue.
13 | *
14 | * It's assumed that for actual sending (SMTP, HTTP API, etc), this service calls a {@link MailApiClient}.
15 | * However, this can be expected to be quite unreliable, so use of Disque as a backing store helps keep track
16 | * of sent/failed state.
17 | */
18 | public class DisqueBackedMailSenderService implements MailSenderService {
19 |
20 | private final DisqueCommands disque;
21 | private final Gson gson;
22 | private final MailApiClient mailApiClient;
23 |
24 | public DisqueBackedMailSenderService(DisqueClient client, MailApiClient mailApiClient) {
25 | this.mailApiClient = mailApiClient;
26 |
27 | // Obtain a Disque connection from DisqueClient
28 | disque = client.connect().sync();
29 |
30 | gson = new Gson();
31 | }
32 |
33 | @Override
34 | public String enqueueMessage(MailMessage message) {
35 | String messageAsJson = gson.toJson(message);
36 |
37 | // Enqueue the message as JSON, setting a TTL of 1 day (will expire off the queue if not sent by then)
38 | String jobId = disque.addjob("mail", messageAsJson, 1, TimeUnit.DAYS);
39 |
40 | return jobId;
41 | }
42 |
43 | @Override
44 | public Result doScheduledSend() {
45 |
46 | Set succeededJobIds = new HashSet<>();
47 | Set failedJobIds = new HashSet<>();
48 |
49 | // Wait up to 100ms for new messages to arrive on the queue
50 | // Retrieve up to 10 messages
51 | // (Ordinarily we'd make these configurable!)
52 | List> jobs = disque.getjobs(100, TimeUnit.MILLISECONDS, 10, "mail");
53 |
54 | for (Job job : jobs) {
55 | String jsonBody = job.getBody();
56 | MailMessage message = gson.fromJson(jsonBody, MailMessage.class);
57 |
58 | try {
59 | mailApiClient.send(message);
60 | succeededJobIds.add(job.getId());
61 | } catch (MailApiClient.MailSendException e) {
62 | failedJobIds.add(job.getId());
63 | }
64 | }
65 |
66 | // For any failed messages, proactively return to the queue
67 | disque.nack(failedJobIds.toArray(new String[failedJobIds.size()]));
68 | // For any successful jobs, mark as completed
69 | disque.ackjob(succeededJobIds.toArray(new String[succeededJobIds.size()]));
70 |
71 | return new Result(succeededJobIds.size(), failedJobIds.size());
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/disque-job-queue/src/test/java/SingleDisqueInstanceTest.java:
--------------------------------------------------------------------------------
1 | import biz.paluch.spinach.DisqueClient;
2 | import biz.paluch.spinach.DisqueURI;
3 | import biz.paluch.spinach.api.AddJobArgs;
4 | import biz.paluch.spinach.api.Job;
5 | import biz.paluch.spinach.api.sync.DisqueCommands;
6 | import org.junit.Before;
7 | import org.junit.Rule;
8 | import org.junit.Test;
9 | import org.testcontainers.containers.GenericContainer;
10 |
11 | import java.util.concurrent.TimeUnit;
12 |
13 | import static org.rnorth.visibleassertions.VisibleAssertions.*;
14 |
15 | /**
16 | * Created by rnorth on 03/01/2016.
17 | */
18 | public class SingleDisqueInstanceTest {
19 |
20 | @Rule
21 | public GenericContainer container = new GenericContainer("richnorth/disque:1.0-rc1")
22 | .withExposedPorts(7711);
23 | private DisqueCommands connection;
24 | private AddJobArgs retryAfter1Second;
25 |
26 | @Before
27 | public void setup() {
28 | context("");
29 | DisqueClient client = new DisqueClient(DisqueURI.create(container.getContainerIpAddress(), container.getMappedPort(7711)));
30 | connection = client.connect().sync();
31 | retryAfter1Second = AddJobArgs.builder().retry(1, TimeUnit.SECONDS).build();
32 |
33 | info("Initialized a fresh Disque instance and client");
34 | }
35 |
36 | @Test
37 | public void testJobStoreAndRetrieve() {
38 |
39 | info("Adding a job to the queue");
40 | connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES);
41 |
42 | info("Getting a job from the queue");
43 | Job job = connection.getjob("main_queue");
44 |
45 | assertEquals("The retrieved job is the same as the one that was added",
46 | "body",
47 | job.getBody());
48 |
49 | info("Acknowledging the job to mark completion");
50 | connection.ackjob(job.getId());
51 | }
52 |
53 | @Test
54 | public void testFailureToAckJobBeforeTimeout() throws InterruptedException {
55 |
56 | info("Adding a job to the queue");
57 | connection.addjob("main_queue", "body", 1, TimeUnit.MINUTES, retryAfter1Second);
58 |
59 | info("Getting a job from the queue");
60 | Job job = connection.getjob("main_queue");
61 |
62 | assertEquals("The retrieved job is the same as the one that was added",
63 | "body",
64 | job.getBody());
65 |
66 | info("Simulating a failure to ack the job before the timeout (1s)");
67 | TimeUnit.SECONDS.sleep(2);
68 |
69 | info("Attempting to get another job from the queue - the RETRY setting for the job means it should have reappeared");
70 | // The timeout specified here is how long the command will wait for a job to appear
71 | Job job2 = connection.getjob(5, TimeUnit.SECONDS, "main_queue");
72 |
73 | assertNotNull("After re-getting the original job is back on the queue", job2);
74 | assertEquals("The retrieved job is the same as the one that was added",
75 | "body",
76 | job2.getBody());
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | org.testcontainers.examples
6 | testcontainers-java-examples
7 | NOVERSION
8 |
9 | redis-backed-cache
10 | disque-job-queue
11 | selenium-container
12 | spring-boot
13 | linked-container
14 |
15 |
16 | pom
17 |
18 |
19 |
20 | ${testcontainers.group}
21 | testcontainers
22 | ${testcontainers.version}
23 |
24 |
25 |
26 | junit
27 | junit
28 | 4.12
29 | provided
30 |
31 |
32 |
33 |
34 | org.projectlombok
35 | lombok
36 | 1.16.6
37 | provided
38 |
39 |
40 |
41 |
42 | org.rnorth.visible-assertions
43 | visible-assertions
44 | 1.0.5
45 | test
46 |
47 |
48 | org.mockito
49 | mockito-all
50 | 1.9.5
51 | test
52 |
53 |
54 |
55 |
56 |
57 |
58 | maven-compiler-plugin
59 | 3.1
60 |
61 | 1.8
62 | 1.8
63 |
64 |
65 |
66 |
67 |
68 |
69 | UTF-8
70 | com.github.testcontainers.testcontainers-java
71 | -SNAPSHOT
72 |
73 |
74 |
75 |
76 |
77 |
78 | Windows
79 |
80 |
81 |
82 | windows-support-SNAPSHOT
83 |
84 |
85 |
86 |
87 |
88 |
89 | jitpack.io
90 | https://jitpack.io
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/disque-job-queue/src/test/java/DisqueBackedMailSenderTest.java:
--------------------------------------------------------------------------------
1 | import biz.paluch.spinach.DisqueClient;
2 | import biz.paluch.spinach.DisqueURI;
3 | import org.junit.Before;
4 | import org.junit.Rule;
5 | import org.junit.Test;
6 | import org.testcontainers.containers.GenericContainer;
7 |
8 | import static org.mockito.Matchers.eq;
9 | import static org.mockito.Mockito.*;
10 | import static org.rnorth.visibleassertions.VisibleAssertions.*;
11 |
12 | /**
13 | * Created by rnorth on 03/01/2016.
14 | */
15 | public class DisqueBackedMailSenderTest {
16 |
17 | @Rule
18 | public GenericContainer container = new GenericContainer("richnorth/disque:1.0-rc1")
19 | .withExposedPorts(7711);
20 | private DisqueClient disqueClient;
21 |
22 | private MailSenderService service;
23 | private MailApiClient mockMailApiClient;
24 |
25 | private MailMessage dummyMessage1;
26 | private MailMessage dummyMessage2;
27 | private MailMessage dummyMessage3;
28 |
29 | @Before
30 | public void setup() {
31 | context("");
32 | disqueClient = new DisqueClient(DisqueURI.create(container.getContainerIpAddress(), container.getMappedPort(7711)));
33 | mockMailApiClient = mock(MailApiClient.class);
34 |
35 | service = new DisqueBackedMailSenderService(disqueClient, mockMailApiClient);
36 |
37 | info("Initialized a fresh Disque instance and service instance");
38 |
39 | dummyMessage1 = new MailMessage("Dummy Message 1", "bob@example.com", "Test body");
40 | dummyMessage2 = new MailMessage("Dummy Message 2", "bob@example.com", "Test body");
41 | dummyMessage3 = new MailMessage("Dummy Message 3", "bob@example.com", "Test body");
42 | }
43 |
44 | @Test
45 | public void testSimpleSuccessfulSending() throws Exception {
46 | service.enqueueMessage(dummyMessage1);
47 | service.enqueueMessage(dummyMessage2);
48 | service.enqueueMessage(dummyMessage3);
49 |
50 | when(mockMailApiClient.send(any(MailMessage.class))).thenReturn(true);
51 |
52 | service.doScheduledSend();
53 |
54 | verify(mockMailApiClient).send(eq(dummyMessage1));
55 | verify(mockMailApiClient).send(eq(dummyMessage2));
56 | verify(mockMailApiClient).send(eq(dummyMessage3));
57 | }
58 |
59 | @Test
60 | public void testRetryOnFailure() throws Exception {
61 | service.enqueueMessage(dummyMessage1);
62 | service.enqueueMessage(dummyMessage2);
63 | service.enqueueMessage(dummyMessage3);
64 |
65 | info("Message 1 will fail to send on the first attempt");
66 | when(mockMailApiClient.send(eq(dummyMessage1))).thenThrow(MailApiClient.MailSendException.class).thenReturn(true);
67 | when(mockMailApiClient.send(eq(dummyMessage2))).thenReturn(true);
68 | when(mockMailApiClient.send(eq(dummyMessage3))).thenReturn(true);
69 |
70 | MailSenderService.Result result;
71 | context("Simulating sending messages");
72 | context("First sending attempt", 4);
73 | result = service.doScheduledSend();
74 | assertEquals("2 messages were 'sent' successfully", 2, result.successfulCount);
75 | assertEquals("1 messages failed", 1, result.failedCount);
76 |
77 | context("Second attempt", 4);
78 | result = service.doScheduledSend();
79 | assertEquals("1 message was 'sent' successfully", 1, result.successfulCount);
80 | assertEquals("0 messages failed", 0, result.failedCount);
81 |
82 | context("Third attempt", 4);
83 | info("No messages should be due to send this time");
84 | result = service.doScheduledSend();
85 | assertEquals("0 messages were 'sent' successfully", 0, result.successfulCount);
86 | assertEquals("0 messages failed", 0, result.failedCount);
87 |
88 | verify(mockMailApiClient, times(2)).send(eq(dummyMessage1));
89 | verify(mockMailApiClient).send(eq(dummyMessage2));
90 | verify(mockMailApiClient).send(eq(dummyMessage3));
91 | }
92 | }
93 |
--------------------------------------------------------------------------------