diff --git a/examples/singleton-container/pom.xml b/examples/singleton-container/pom.xml
new file mode 100644
index 00000000000..710c32d6b80
--- /dev/null
+++ b/examples/singleton-container/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ testcontainers-java-examples
+ org.testcontainers.examples
+ NOVERSION
+
+ 4.0.0
+
+ singleton-container
+
+
+
+ redis.clients
+ jedis
+ 3.0.1
+
+
+ com.google.code.gson
+ gson
+ 2.8.5
+
+
+ com.google.guava
+ guava
+ 23.0
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.25
+ provided
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.3
+ test
+
+
+
diff --git a/examples/singleton-container/src/main/java/com/example/cache/Cache.java b/examples/singleton-container/src/main/java/com/example/cache/Cache.java
new file mode 100644
index 00000000000..5c5ba253d6e
--- /dev/null
+++ b/examples/singleton-container/src/main/java/com/example/cache/Cache.java
@@ -0,0 +1,10 @@
+package com.example.cache;
+
+import java.util.Optional;
+
+public interface Cache {
+
+ void put(String key, Object value);
+
+ Optional get(String key, Class expectedClass);
+}
\ No newline at end of file
diff --git a/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java b/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java
new file mode 100644
index 00000000000..12585cafe16
--- /dev/null
+++ b/examples/singleton-container/src/main/java/com/example/cache/RedisBackedCache.java
@@ -0,0 +1,34 @@
+package com.example.cache;
+
+import com.google.gson.Gson;
+import redis.clients.jedis.Jedis;
+
+import java.util.Optional;
+
+public class RedisBackedCache implements Cache {
+
+ private final Jedis jedis;
+ private final String cacheName;
+ private final Gson gson;
+
+ public RedisBackedCache(Jedis jedis, String cacheName) {
+ this.jedis = jedis;
+ this.cacheName = cacheName;
+ this.gson = new Gson();
+ }
+
+ public void put(String key, Object value) {
+ String jsonValue = gson.toJson(value);
+ this.jedis.hset(this.cacheName, key, jsonValue);
+ }
+
+ public Optional get(String key, Class expectedClass) {
+ String foundJson = this.jedis.hget(this.cacheName, key);
+
+ if (foundJson == null) {
+ return Optional.empty();
+ }
+
+ return Optional.of(gson.fromJson(foundJson, expectedClass));
+ }
+}
diff --git a/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java
new file mode 100644
index 00000000000..6daff319753
--- /dev/null
+++ b/examples/singleton-container/src/test/java/com/example/AbstractIntegrationTest.java
@@ -0,0 +1,13 @@
+package com.example;
+
+import org.testcontainers.containers.GenericContainer;
+
+public abstract class AbstractIntegrationTest {
+
+ public static final GenericContainer redis = new GenericContainer("redis:3.0.6")
+ .withExposedPorts(6379);
+
+ static {
+ redis.start();
+ }
+}
diff --git a/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java
new file mode 100644
index 00000000000..08a5ebfe2f5
--- /dev/null
+++ b/examples/singleton-container/src/test/java/com/example/BarConcreteTestClass.java
@@ -0,0 +1,34 @@
+package com.example;
+
+import com.example.cache.Cache;
+import com.example.cache.RedisBackedCache;
+import org.junit.Before;
+import org.junit.Test;
+import redis.clients.jedis.Jedis;
+
+import java.util.Optional;
+
+import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals;
+import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue;
+
+public class BarConcreteTestClass extends AbstractIntegrationTest {
+
+ private Cache cache;
+
+ @Before
+ public void setUp() {
+ Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379));
+
+ cache = new RedisBackedCache(jedis, "bar");
+ }
+
+ @Test
+ public void testInsertValue() {
+ cache.put("bar", "BAR");
+ Optional foundObject = cache.get("bar", String.class);
+
+ assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent());
+ assertEquals("When accessing the value of a retrieved object, the value must be the same", "BAR", foundObject.get());
+ }
+
+}
diff --git a/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java
new file mode 100644
index 00000000000..1196348b258
--- /dev/null
+++ b/examples/singleton-container/src/test/java/com/example/FooConcreteTestClass.java
@@ -0,0 +1,33 @@
+package com.example;
+
+import com.example.cache.Cache;
+import com.example.cache.RedisBackedCache;
+import org.junit.Before;
+import org.junit.Test;
+import redis.clients.jedis.Jedis;
+
+import java.util.Optional;
+
+import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals;
+import static org.rnorth.visibleassertions.VisibleAssertions.assertTrue;
+
+public class FooConcreteTestClass extends AbstractIntegrationTest {
+
+ private Cache cache;
+
+ @Before
+ public void setUp() throws Exception {
+ Jedis jedis = new Jedis(redis.getContainerIpAddress(), redis.getMappedPort(6379));
+
+ cache = new RedisBackedCache(jedis, "foo");
+ }
+
+ @Test
+ public void testInsertValue() {
+ cache.put("foo", "FOO");
+ Optional foundObject = cache.get("foo", String.class);
+
+ assertTrue("When inserting an object into the cache, it can be retrieved", foundObject.isPresent());
+ assertEquals("When accessing the value of a retrieved object, the value must be the same", "FOO", foundObject.get());
+ }
+}