├── .github └── workflows │ ├── linux-build-exercises-gradle.yml │ └── linux-build-exercises-maven.yml ├── .gitignore ├── README.md ├── exercises ├── 01-redis-generic-container │ ├── instructions.md │ ├── solution │ │ ├── .mvn │ │ │ └── wrapper │ │ │ │ ├── MavenWrapperDownloader.java │ │ │ │ ├── maven-wrapper.jar │ │ │ │ └── maven-wrapper.properties │ │ ├── build.gradle │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── mvnw │ │ ├── mvnw.cmd │ │ ├── pom.xml │ │ ├── settings.gradle │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── bmuschko │ │ │ │ └── testcontainers │ │ │ │ └── RedisRepository.java │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── bmuschko │ │ │ └── testcontainers │ │ │ └── RedisRepositoryIntegrationTest.java │ └── start │ │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ │ ├── build.gradle │ │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── mvnw │ │ ├── mvnw.cmd │ │ ├── pom.xml │ │ ├── settings.gradle │ │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── bmuschko │ │ │ └── testcontainers │ │ │ └── RedisRepository.java │ │ └── test │ │ └── java │ │ └── com │ │ └── bmuschko │ │ └── testcontainers │ │ └── RedisRepositoryIntegrationTest.java ├── 02-postgesql-database-container │ ├── instructions.md │ ├── solution │ │ ├── .mvn │ │ │ └── wrapper │ │ │ │ ├── MavenWrapperDownloader.java │ │ │ │ ├── maven-wrapper.jar │ │ │ │ └── maven-wrapper.properties │ │ ├── build.gradle │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── mvnw │ │ ├── mvnw.cmd │ │ ├── pom.xml │ │ ├── settings.gradle │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── bmuschko │ │ │ │ └── testcontainers │ │ │ │ ├── Item.java │ │ │ │ ├── WarehouseException.java │ │ │ │ ├── WarehouseRepository.java │ │ │ │ └── WarehouseRepositoryImpl.java │ │ │ └── test │ │ │ ├── java │ │ │ └── com │ │ │ │ └── bmuschko │ │ │ │ └── testcontainers │ │ │ │ └── WarehouseRepositoryIntegrationTest.java │ │ │ └── resources │ │ │ └── warehouse.sql │ └── start │ │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ │ ├── build.gradle │ │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── mvnw │ │ ├── mvnw.cmd │ │ ├── pom.xml │ │ ├── settings.gradle │ │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── bmuschko │ │ │ └── testcontainers │ │ │ ├── Item.java │ │ │ ├── WarehouseException.java │ │ │ ├── WarehouseRepository.java │ │ │ └── WarehouseRepositoryImpl.java │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── bmuschko │ │ │ └── testcontainers │ │ │ └── WarehouseRepositoryIntegrationTest.java │ │ └── resources │ │ └── warehouse.sql ├── 03-multi-container │ ├── instructions.md │ ├── solution │ │ ├── docker-compose │ │ │ ├── .mvn │ │ │ │ └── wrapper │ │ │ │ │ ├── MavenWrapperDownloader.java │ │ │ │ │ ├── maven-wrapper.jar │ │ │ │ │ └── maven-wrapper.properties │ │ │ ├── build.gradle │ │ │ ├── gradle │ │ │ │ └── wrapper │ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── mvnw │ │ │ ├── mvnw.cmd │ │ │ ├── pom.xml │ │ │ ├── settings.gradle │ │ │ └── src │ │ │ │ ├── main │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── bmuschko │ │ │ │ │ └── testcontainers │ │ │ │ │ ├── model │ │ │ │ │ └── warehouse │ │ │ │ │ │ └── Product.java │ │ │ │ │ ├── repository │ │ │ │ │ └── warehouse │ │ │ │ │ │ ├── db │ │ │ │ │ │ ├── UsernamePasswordCredentials.java │ │ │ │ │ │ ├── WarehouseDatabaseException.java │ │ │ │ │ │ ├── WarehouseDatabaseRepository.java │ │ │ │ │ │ └── WarehouseDatabaseRepositoryImpl.java │ │ │ │ │ │ └── index │ │ │ │ │ │ ├── ProductIndexException.java │ │ │ │ │ │ ├── ProductIndexRepository.java │ │ │ │ │ │ └── ProductIndexRepositoryImpl.java │ │ │ │ │ └── service │ │ │ │ │ ├── WarehouseService.java │ │ │ │ │ └── WarehouseServiceImpl.java │ │ │ │ └── test │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── bmuschko │ │ │ │ │ └── testcontainers │ │ │ │ │ └── service │ │ │ │ │ └── WarehouseServiceImplIntegrationTest.java │ │ │ │ └── resources │ │ │ │ ├── warehouse-test.yml │ │ │ │ └── warehouse.sql │ │ └── multi-container │ │ │ ├── .mvn │ │ │ └── wrapper │ │ │ │ ├── MavenWrapperDownloader.java │ │ │ │ ├── maven-wrapper.jar │ │ │ │ └── maven-wrapper.properties │ │ │ ├── build.gradle │ │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── mvnw │ │ │ ├── mvnw.cmd │ │ │ ├── pom.xml │ │ │ ├── settings.gradle │ │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── bmuschko │ │ │ │ └── testcontainers │ │ │ │ ├── model │ │ │ │ └── warehouse │ │ │ │ │ └── Product.java │ │ │ │ ├── repository │ │ │ │ └── warehouse │ │ │ │ │ ├── db │ │ │ │ │ ├── UsernamePasswordCredentials.java │ │ │ │ │ ├── WarehouseDatabaseException.java │ │ │ │ │ ├── WarehouseDatabaseRepository.java │ │ │ │ │ └── WarehouseDatabaseRepositoryImpl.java │ │ │ │ │ └── index │ │ │ │ │ ├── ProductIndexException.java │ │ │ │ │ ├── ProductIndexRepository.java │ │ │ │ │ └── ProductIndexRepositoryImpl.java │ │ │ │ └── service │ │ │ │ ├── WarehouseService.java │ │ │ │ └── WarehouseServiceImpl.java │ │ │ └── test │ │ │ ├── java │ │ │ └── com │ │ │ │ └── bmuschko │ │ │ │ └── testcontainers │ │ │ │ └── service │ │ │ │ └── WarehouseServiceImplIntegrationTest.java │ │ │ └── resources │ │ │ └── warehouse.sql │ └── start │ │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ │ ├── build.gradle │ │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── mvnw │ │ ├── mvnw.cmd │ │ ├── pom.xml │ │ ├── settings.gradle │ │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── bmuschko │ │ │ └── testcontainers │ │ │ ├── model │ │ │ └── warehouse │ │ │ │ └── Product.java │ │ │ ├── repository │ │ │ └── warehouse │ │ │ │ ├── db │ │ │ │ ├── UsernamePasswordCredentials.java │ │ │ │ ├── WarehouseDatabaseException.java │ │ │ │ ├── WarehouseDatabaseRepository.java │ │ │ │ └── WarehouseDatabaseRepositoryImpl.java │ │ │ │ └── index │ │ │ │ ├── ProductIndexException.java │ │ │ │ ├── ProductIndexRepository.java │ │ │ │ └── ProductIndexRepositoryImpl.java │ │ │ └── service │ │ │ ├── WarehouseService.java │ │ │ └── WarehouseServiceImpl.java │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── bmuschko │ │ │ └── testcontainers │ │ │ └── service │ │ │ └── WarehouseServiceImplIntegrationTest.java │ │ └── resources │ │ ├── warehouse-test.yml │ │ └── warehouse.sql ├── 04-generic-container │ ├── instructions.md │ ├── solution │ │ ├── dockerfile │ │ │ ├── .mvn │ │ │ │ └── wrapper │ │ │ │ │ ├── MavenWrapperDownloader.java │ │ │ │ │ ├── maven-wrapper.jar │ │ │ │ │ └── maven-wrapper.properties │ │ │ ├── build.gradle │ │ │ ├── gradle │ │ │ │ └── wrapper │ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── mvnw │ │ │ ├── mvnw.cmd │ │ │ ├── pom.xml │ │ │ ├── settings.gradle │ │ │ └── src │ │ │ │ ├── main │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── bmuschko │ │ │ │ │ └── testcontainers │ │ │ │ │ ├── NginxService.java │ │ │ │ │ └── NginxServiceImpl.java │ │ │ │ └── test │ │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── bmuschko │ │ │ │ │ └── testcontainers │ │ │ │ │ └── NginxServiceIntegrationTest.java │ │ │ │ └── resources │ │ │ │ └── Dockerfile │ │ ├── instructions │ │ │ ├── .mvn │ │ │ │ └── wrapper │ │ │ │ │ ├── MavenWrapperDownloader.java │ │ │ │ │ ├── maven-wrapper.jar │ │ │ │ │ └── maven-wrapper.properties │ │ │ ├── build.gradle │ │ │ ├── gradle │ │ │ │ └── wrapper │ │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── mvnw │ │ │ ├── mvnw.cmd │ │ │ ├── pom.xml │ │ │ ├── settings.gradle │ │ │ └── src │ │ │ │ ├── main │ │ │ │ └── java │ │ │ │ │ └── com │ │ │ │ │ └── bmuschko │ │ │ │ │ └── testcontainers │ │ │ │ │ ├── NginxService.java │ │ │ │ │ └── NginxServiceImpl.java │ │ │ │ └── test │ │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── bmuschko │ │ │ │ └── testcontainers │ │ │ │ └── NginxServiceIntegrationTest.java │ │ └── prebuilt │ │ │ ├── .mvn │ │ │ └── wrapper │ │ │ │ ├── MavenWrapperDownloader.java │ │ │ │ ├── maven-wrapper.jar │ │ │ │ └── maven-wrapper.properties │ │ │ ├── build.gradle │ │ │ ├── gradle │ │ │ └── wrapper │ │ │ │ ├── gradle-wrapper.jar │ │ │ │ └── gradle-wrapper.properties │ │ │ ├── gradlew │ │ │ ├── gradlew.bat │ │ │ ├── mvnw │ │ │ ├── mvnw.cmd │ │ │ ├── pom.xml │ │ │ ├── settings.gradle │ │ │ └── src │ │ │ ├── main │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── bmuschko │ │ │ │ └── testcontainers │ │ │ │ ├── NginxService.java │ │ │ │ └── NginxServiceImpl.java │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── bmuschko │ │ │ └── testcontainers │ │ │ └── NginxServiceIntegrationTest.java │ └── start │ │ ├── .mvn │ │ └── wrapper │ │ │ ├── MavenWrapperDownloader.java │ │ │ ├── maven-wrapper.jar │ │ │ └── maven-wrapper.properties │ │ ├── build.gradle │ │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ │ ├── gradlew │ │ ├── gradlew.bat │ │ ├── mvnw │ │ ├── mvnw.cmd │ │ ├── pom.xml │ │ ├── settings.gradle │ │ └── src │ │ ├── main │ │ └── java │ │ │ └── com │ │ │ └── bmuschko │ │ │ └── testcontainers │ │ │ ├── NginxService.java │ │ │ └── NginxServiceImpl.java │ │ └── test │ │ ├── java │ │ └── com │ │ │ └── bmuschko │ │ │ └── testcontainers │ │ │ └── NginxServiceIntegrationTest.java │ │ └── resources │ │ └── Dockerfile └── 05-ci-github-actions │ ├── instructions.md │ └── solution │ ├── images │ ├── github-actions-execution.png │ └── github-repository-creation.png │ └── solution.md ├── prerequisites └── instructions.md └── slides.pdf /.github/workflows/linux-build-exercises-gradle.yml: -------------------------------------------------------------------------------- 1 | name: Build Exercises with Gradle [Linux] 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | name: Build with Gradle 7 | runs-on: ubuntu-22.04 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v1 11 | - name: Set up Java 12 | uses: actions/setup-java@v1 13 | with: 14 | java-version: 11 15 | - name: Exercise 1 - Redis Generic Container 16 | uses: gradle/gradle-build-action@v2 17 | with: 18 | arguments: build 19 | build-root-directory: exercises/01-redis-generic-container/solution 20 | - name: Exercise 2 - PostgreSQL Database Container 21 | uses: gradle/gradle-build-action@v2 22 | with: 23 | arguments: build 24 | build-root-directory: exercises/02-postgesql-database-container/solution 25 | - name: Exercise 3 - Multi-Container 26 | uses: gradle/gradle-build-action@v2 27 | with: 28 | arguments: build 29 | build-root-directory: exercises/03-multi-container/solution/multi-container 30 | - name: Exercise 3 - Docker Compose 31 | uses: gradle/gradle-build-action@v2 32 | with: 33 | arguments: build 34 | build-root-directory: exercises/03-multi-container/solution/docker-compose 35 | - name: Exercise 4 - Generic Prebuilt Container 36 | uses: gradle/gradle-build-action@v2 37 | with: 38 | arguments: build 39 | build-root-directory: exercises/04-generic-container/solution/prebuilt 40 | - name: Exercise 4 - Generic Container Built By Dockerfile 41 | uses: gradle/gradle-build-action@v2 42 | with: 43 | arguments: build 44 | build-root-directory: exercises/04-generic-container/solution/dockerfile 45 | - name: Exercise 4 - Generic Container Built By Individual Instructions 46 | uses: gradle/gradle-build-action@v2 47 | with: 48 | arguments: build 49 | build-root-directory: exercises/04-generic-container/solution/instructions -------------------------------------------------------------------------------- /.github/workflows/linux-build-exercises-maven.yml: -------------------------------------------------------------------------------- 1 | name: Build Exercises with Maven [Linux] 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | build: 6 | name: Build with Maven 7 | runs-on: ubuntu-22.04 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v1 11 | - name: Set up Java 12 | uses: actions/setup-java@v1 13 | with: 14 | java-version: 11 15 | - name: Exercise 1 - Redis Generic Container 16 | run: cd exercises/01-redis-generic-container/solution;./mvnw test 17 | - name: Exercise 2 - PostgreSQL Database Container 18 | run: cd exercises/02-postgesql-database-container/solution;./mvnw test 19 | - name: Exercise 3 - Multi-Container 20 | run: cd exercises/03-multi-container/solution/multi-container;./mvnw test 21 | - name: Exercise 3 - Docker Compose 22 | run: cd exercises/03-multi-container/solution/docker-compose;./mvnw test 23 | - name: Exercise 4 - Generic Prebuilt Container 24 | run: cd exercises/04-generic-container/solution/prebuilt;./mvnw test 25 | - name: Exercise 4 - Generic Container Built By Dockerfile 26 | run: cd exercises/04-generic-container/solution/dockerfile;./mvnw test 27 | - name: Exercise 4 - Generic Container Built By Individual Instructions 28 | run: cd exercises/04-generic-container/solution/instructions;./mvnw test -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | out 4 | build 5 | target 6 | .gradle 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Integration Testing with Docker and Testcontainers 2 | 3 | The process of developing an application typically involves the work of multiple engineers, and that work is usually broken down into several modules. There is always a risk that modules developed by different engineers will not sync when integrating with each other for use in production. Integration testing helps minimize this risk by enabling better integration of modules. But many organizations struggle with maintaining tests that require more complex setup procedures. As a result, integration tests become flaky and unreliable and require manual intervention. In an automate-all-the-things world, this costs organizations time and money. 4 | 5 | Join expert Benjamin Muschko to discover how to set up and run integration and functional tests with the help of the open source library Testcontainers. You will learn how to stand up lightweight, disposable Docker instances running your application as reliable test fixtures. 6 | 7 | ## Prerequisites 8 | 9 | Please make sure to follow the [instructions](./prerequisites/instructions.md) for setting up your environment before joining the training. 10 | 11 | ## Exercises 12 | 13 | All [exercises](./exercises) are numbered and live in dedicated directories starting with the name `exercise-`. You'll find instructions for each exercise in each folder. Solutions are available in the `solution` folder. Try to solve each exercise yourself before having a look at the solution. 14 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 1 2 | 3 | In this exercise, you will learn how to use Testcontainers to run a Redis database as test fixture managed by a JUnit 5 test class. You can find the initial code in the `start` directory. The production source class under test is named `RedisRepository.java` under the directory `src/main/java`. The test class `RedisRepositoryIntegrationTest.java` under the directory `src/test/java` already implements a test case. 4 | 5 | > **_NOTE:_** You can choose to run the build using Maven or Gradle. Pick the tool you are most comfortable with. 6 | 7 | 1. Add the JUnit Jupiter and Testcontainers dependencies. Use the version 5.8.1 for JUnit, use version 1.17.3 for Testcontainers. 8 | 2. To ensure that Testcontainers can log its messages to the console, add a SLF4J implementation as dependency. 9 | 3. In the test class, create a new container instance of type `org.testcontainers.containers.GenericContainer`. Declare the image `redis:6.2.6-alpine` and the exposed port `6379` for the container. The container should be created and destroyed for every test method. 10 | 4. Instantiate a class of `com.bmuschko.testcontainers.RedisRepository` and assign it to the variable named `redisRepository`. Determine the runtime value for the host and port of the container and provide it in the constructor of the class. 11 | 5. Run the test and verify its correct behavior. For Maven the command is `./mvnw test`, for Gradle the command is `./gradlew test`. The console output should indicate that the container has been created and destroyed after the test finished. 12 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/solution/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/solution/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/01-redis-generic-container/solution/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/solution/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/solution/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group = 'com.bmuschko' 4 | version = '1.0.0' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_11 7 | targetCompatibility = JavaVersion.VERSION_11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | implementation 'redis.clients:jedis:3.7.0' 15 | 16 | def junitJupiterVersion = '5.8.1' 17 | testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" 18 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" 19 | 20 | testImplementation platform('org.testcontainers:testcontainers-bom:1.17.3') 21 | testImplementation 'org.testcontainers:junit-jupiter' 22 | testImplementation 'org.slf4j:slf4j-simple:1.7.32' 23 | } 24 | 25 | test { 26 | useJUnitPlatform() 27 | testLogging { 28 | showStandardStreams = true 29 | } 30 | } -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/solution/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/01-redis-generic-container/solution/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/solution/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/solution/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/solution/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | 7 | com.bmuschko 8 | redis-generic-container 9 | 1.0.0 10 | 11 | 12 | 11 13 | UTF-8 14 | 3.7.0 15 | 5.8.1 16 | 17 | 18 | 19 | 20 | redis.clients 21 | jedis 22 | ${jedis.version} 23 | 24 | 25 | org.junit.jupiter 26 | junit-jupiter-api 27 | ${junit.jupiter.version} 28 | test 29 | 30 | 31 | org.junit.jupiter 32 | junit-jupiter-engine 33 | ${junit.jupiter.version} 34 | test 35 | 36 | 37 | org.testcontainers 38 | junit-jupiter 39 | test 40 | 41 | 42 | org.slf4j 43 | slf4j-simple 44 | 1.7.32 45 | test 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.testcontainers 53 | testcontainers-bom 54 | 1.17.3 55 | pom 56 | import 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.apache.maven.plugins 66 | maven-compiler-plugin 67 | 3.8.1 68 | 69 | ${jdk.version} 70 | ${jdk.version} 71 | ${project.build.sourceEncoding} 72 | 73 | 74 | 75 | maven-surefire-plugin 76 | 2.22.2 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/solution/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'redis-generic-container' -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/solution/src/main/java/com/bmuschko/testcontainers/RedisRepository.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import redis.clients.jedis.Jedis; 4 | 5 | public class RedisRepository { 6 | private final Jedis jedis; 7 | 8 | public RedisRepository(String host, Integer port) { 9 | this.jedis = new Jedis(host, port); 10 | } 11 | 12 | public void put(String key, String value) { 13 | jedis.set(key, value); 14 | } 15 | 16 | public String get(String key) { 17 | return jedis.get(key); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/solution/src/test/java/com/bmuschko/testcontainers/RedisRepositoryIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.testcontainers.containers.GenericContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | import org.testcontainers.junit.jupiter.Testcontainers; 8 | import org.testcontainers.utility.DockerImageName; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | 12 | @Testcontainers 13 | public class RedisRepositoryIntegrationTest { 14 | private RedisRepository redisRepository; 15 | 16 | @Container 17 | private final GenericContainer redis = new GenericContainer(DockerImageName.parse("redis:6.2.6-alpine")).withExposedPorts(6379); 18 | 19 | @BeforeEach 20 | public void setUp() { 21 | String host = redis.getHost(); 22 | Integer port = redis.getFirstMappedPort(); 23 | redisRepository = new RedisRepository(host, port); 24 | } 25 | 26 | @Test 27 | public void testSimplePutAndGet() { 28 | String key = "test"; 29 | String value = "example"; 30 | 31 | redisRepository.put(key, value); 32 | 33 | String retrieved = redisRepository.get(key); 34 | assertEquals(value, retrieved); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/start/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/start/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/01-redis-generic-container/start/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/start/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/start/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group = 'com.bmuschko' 4 | version = '1.0.0' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_11 7 | targetCompatibility = JavaVersion.VERSION_11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | implementation 'redis.clients:jedis:3.7.0' 15 | } 16 | 17 | test { 18 | useJUnitPlatform() 19 | testLogging { 20 | showStandardStreams = true 21 | } 22 | } -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/start/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/01-redis-generic-container/start/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/start/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/start/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/start/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | 7 | com.bmuschko 8 | redis-generic-container 9 | 1.0.0 10 | 11 | 12 | 11 13 | UTF-8 14 | 15 | 16 | 17 | 18 | redis.clients 19 | jedis 20 | ${jedis.version} 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | org.apache.maven.plugins 29 | maven-compiler-plugin 30 | 3.8.1 31 | 32 | ${jdk.version} 33 | ${jdk.version} 34 | ${project.build.sourceEncoding} 35 | 36 | 37 | 38 | maven-surefire-plugin 39 | 2.22.2 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/start/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'redis-generic-container' -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/start/src/main/java/com/bmuschko/testcontainers/RedisRepository.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import redis.clients.jedis.Jedis; 4 | 5 | public class RedisRepository { 6 | private final Jedis jedis; 7 | 8 | public RedisRepository(String host, Integer port) { 9 | this.jedis = new Jedis(host, port); 10 | } 11 | 12 | public void put(String key, String value) { 13 | jedis.set(key, value); 14 | } 15 | 16 | public String get(String key) { 17 | return jedis.get(key); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /exercises/01-redis-generic-container/start/src/test/java/com/bmuschko/testcontainers/RedisRepositoryIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertEquals; 6 | 7 | @Testcontainers 8 | public class RedisRepositoryIntegrationTest { 9 | private RedisRepository redisRepository; 10 | 11 | @Test 12 | public void testSimplePutAndGet() { 13 | String key = "test"; 14 | String value = "example"; 15 | 16 | redisRepository.put(key, value); 17 | 18 | String retrieved = redisRepository.get(key); 19 | assertEquals(value, retrieved); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 2 2 | 3 | In this exercise, you will learn how to use Testcontainers to bring up a PostgreSQL database with seed data. You can find the initial code in the `start` directory. The production source class under test is named `WarehouseRepository.java` under the directory `src/main/java`. The test class `WarehouseRepositoryIntegrationTest.java` under the directory `src/test/java` already implements a test case. 4 | 5 | > **_NOTE:_** You can choose to run the build using Maven or Gradle. Pick the tool you are most comfortable with. 6 | 7 | 1. Add the Testcontainers dependency that supports creating a PostgreSQL container. Add the dependency for the PostgreSQL JDBC driver with version `42.5.0`. 8 | 2. In the test class, create an instance of `org.testcontainers.containers.PostgreSQLContainer`. Assign the image `postgres:9.6.12` and the database name `warehouse`. Upon creation, the container instance should run the script `warehouse.sql` to create the database schema. 9 | 3. Create an instance of `WarehouseRepositoryImpl` and inject the JDBC URL, username, and password into the constructor before every test method. 10 | 4. Run the test and verify its correct behavior. For Maven the command is `./mvnw test`, for Gradle the command is `./gradlew test`. The console output should indicate that the container has been created and destroyed after the test finished. 11 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/02-postgesql-database-container/solution/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group = 'com.bmuschko' 4 | version = '1.0.0' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_11 7 | targetCompatibility = JavaVersion.VERSION_11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | runtimeOnly 'org.postgresql:postgresql:42.5.0' 15 | 16 | def junitJupiterVersion = '5.8.1' 17 | testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" 18 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" 19 | 20 | testImplementation platform('org.testcontainers:testcontainers-bom:1.17.3') 21 | testImplementation 'org.testcontainers:junit-jupiter' 22 | testImplementation 'org.testcontainers:postgresql' 23 | testImplementation 'org.slf4j:slf4j-simple:1.7.32' 24 | } 25 | 26 | test { 27 | useJUnitPlatform() 28 | testLogging { 29 | showStandardStreams = true 30 | } 31 | } -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/02-postgesql-database-container/solution/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | 7 | com.bmuschko 8 | postgresql-database-container 9 | 1.0.0 10 | 11 | 12 | 11 13 | UTF-8 14 | 42.5.0 15 | 5.8.1 16 | 17 | 18 | 19 | 20 | org.postgresql 21 | postgresql 22 | ${postgresql.version} 23 | runtime 24 | 25 | 26 | org.junit.jupiter 27 | junit-jupiter-api 28 | ${junit.jupiter.version} 29 | test 30 | 31 | 32 | org.junit.jupiter 33 | junit-jupiter-engine 34 | ${junit.jupiter.version} 35 | test 36 | 37 | 38 | org.testcontainers 39 | junit-jupiter 40 | test 41 | 42 | 43 | org.testcontainers 44 | postgresql 45 | test 46 | 47 | 48 | org.slf4j 49 | slf4j-simple 50 | 1.7.32 51 | test 52 | 53 | 54 | 55 | 56 | 57 | 58 | org.testcontainers 59 | testcontainers-bom 60 | 1.17.3 61 | pom 62 | import 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | org.apache.maven.plugins 72 | maven-compiler-plugin 73 | 3.8.1 74 | 75 | ${jdk.version} 76 | ${jdk.version} 77 | ${project.build.sourceEncoding} 78 | 79 | 80 | 81 | maven-surefire-plugin 82 | 2.22.2 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'postgresql-database-container' -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/src/main/java/com/bmuschko/testcontainers/Item.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class Item { 6 | private Long id; 7 | private String name; 8 | private BigDecimal price; 9 | 10 | public Long getId() { 11 | return id; 12 | } 13 | 14 | public void setId(Long id) { 15 | this.id = id; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | public BigDecimal getPrice() { 27 | return price; 28 | } 29 | 30 | public void setPrice(BigDecimal price) { 31 | this.price = price; 32 | } 33 | 34 | @Override 35 | public boolean equals(Object o) { 36 | if (this == o) { 37 | return true; 38 | } 39 | if (!(o instanceof Item)) { 40 | return false; 41 | } 42 | 43 | Item item = (Item) o; 44 | 45 | if (id != null ? !id.equals(item.id) : item.id != null) { 46 | return false; 47 | } 48 | if (name != null ? !name.equals(item.name) : item.name != null) { 49 | return false; 50 | } 51 | return price != null ? price.equals(item.price) : item.price == null; 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | int result = id != null ? id.hashCode() : 0; 57 | result = 31 * result + (name != null ? name.hashCode() : 0); 58 | result = 31 * result + (price != null ? price.hashCode() : 0); 59 | return result; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/src/main/java/com/bmuschko/testcontainers/WarehouseException.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | public class WarehouseException extends RuntimeException { 4 | public WarehouseException(String message, Throwable cause) { 5 | super(message, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/src/main/java/com/bmuschko/testcontainers/WarehouseRepository.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | public interface WarehouseRepository { 4 | void insertItem(Item item); 5 | Item getItem(Long id); 6 | } 7 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/src/main/java/com/bmuschko/testcontainers/WarehouseRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.PreparedStatement; 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | import java.util.Properties; 10 | 11 | public class WarehouseRepositoryImpl implements WarehouseRepository { 12 | private final String jdbcUrl; 13 | private final String username; 14 | private final String password; 15 | 16 | public WarehouseRepositoryImpl(String jdbcUrl, String username, String password) { 17 | this.jdbcUrl = jdbcUrl; 18 | this.username = username; 19 | this.password = password; 20 | } 21 | 22 | @Override 23 | public void insertItem(Item item) { 24 | Connection connection = null; 25 | PreparedStatement pstmt = null; 26 | ResultSet rs = null; 27 | 28 | try { 29 | connection = createConnection(); 30 | pstmt = connection.prepareStatement("INSERT INTO item (name, price) VALUES (?, ?) RETURNING ID"); 31 | pstmt.setString(1, item.getName()); 32 | pstmt.setBigDecimal(2, item.getPrice()); 33 | rs = pstmt.executeQuery(); 34 | 35 | while (rs.next()) { 36 | item.setId(rs.getLong(1)); 37 | } 38 | } catch (SQLException e) { 39 | throw new WarehouseException("Unable to insert item", e); 40 | } finally { 41 | closeResultSet(rs); 42 | closeStatement(pstmt); 43 | closeConnection(connection); 44 | } 45 | } 46 | 47 | @Override 48 | public Item getItem(Long id) { 49 | Connection connection = null; 50 | PreparedStatement pstmt = null; 51 | ResultSet rs = null; 52 | Item item = null; 53 | 54 | try { 55 | connection = createConnection(); 56 | pstmt = connection.prepareStatement("SELECT id, name, price FROM item WHERE id = ?"); 57 | pstmt.setLong(1, id); 58 | rs = pstmt.executeQuery(); 59 | 60 | while (rs.next()) { 61 | item = new Item(); 62 | item.setId(rs.getLong(1)); 63 | item.setName(rs.getString(2)); 64 | item.setPrice(rs.getBigDecimal(3)); 65 | } 66 | 67 | return item; 68 | } catch (SQLException e) { 69 | throw new WarehouseException("Unable to insert item", e); 70 | } finally { 71 | closeResultSet(rs); 72 | closeStatement(pstmt); 73 | closeConnection(connection); 74 | } 75 | } 76 | 77 | private Connection createConnection() { 78 | Properties props = new Properties(); 79 | props.setProperty("user", username); 80 | props.setProperty("password", password); 81 | 82 | try { 83 | return DriverManager.getConnection(jdbcUrl, props); 84 | } catch (SQLException e) { 85 | throw new WarehouseException("Unable to establish connection to database", e); 86 | } 87 | } 88 | 89 | private void closeConnection(Connection connection) { 90 | try { 91 | if (connection != null && !connection.isClosed()) { 92 | connection.close(); 93 | } 94 | } catch (SQLException e) { 95 | // ignore 96 | } 97 | } 98 | 99 | private void closeStatement(Statement statement) { 100 | try { 101 | if (statement != null && !statement.isClosed()) { 102 | statement.close(); 103 | } 104 | } catch (SQLException e) { 105 | // ignore 106 | } 107 | } 108 | 109 | private void closeResultSet(ResultSet resultSet) { 110 | try { 111 | if (resultSet != null && !resultSet.isClosed()) { 112 | resultSet.close(); 113 | } 114 | } catch (SQLException e) { 115 | // ignore 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/src/test/java/com/bmuschko/testcontainers/WarehouseRepositoryIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.testcontainers.containers.JdbcDatabaseContainer; 6 | import org.testcontainers.containers.PostgreSQLContainer; 7 | import org.testcontainers.junit.jupiter.Container; 8 | import org.testcontainers.junit.jupiter.Testcontainers; 9 | 10 | import java.math.BigDecimal; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertEquals; 13 | 14 | @Testcontainers 15 | public class WarehouseRepositoryIntegrationTest { 16 | private WarehouseRepository warehouseRepository; 17 | 18 | @Container 19 | private final JdbcDatabaseContainer postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.12") 20 | .withInitScript("warehouse.sql") 21 | .withDatabaseName("warehouse"); 22 | 23 | @BeforeEach 24 | public void setUp() { 25 | String jdbcUrl = postgreSQLContainer.getJdbcUrl(); 26 | String username = postgreSQLContainer.getUsername(); 27 | String password = postgreSQLContainer.getPassword(); 28 | warehouseRepository = new WarehouseRepositoryImpl(jdbcUrl, username, password); 29 | } 30 | 31 | @Test 32 | public void testInsertAndGetItem() { 33 | Item clock = new Item(); 34 | clock.setName("Clock"); 35 | clock.setPrice(new BigDecimal(39.99)); 36 | 37 | warehouseRepository.insertItem(clock); 38 | 39 | Item retrieved = warehouseRepository.getItem(clock.getId()); 40 | assertEquals(clock, retrieved); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/solution/src/test/resources/warehouse.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS ITEM; 2 | 3 | CREATE TABLE ITEM( 4 | ID SERIAL PRIMARY KEY, 5 | NAME TEXT NOT NULL, 6 | PRICE NUMERIC NOT NULL 7 | ); -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/02-postgesql-database-container/start/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group = 'com.bmuschko' 4 | version = '1.0.0' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_11 7 | targetCompatibility = JavaVersion.VERSION_11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | def junitJupiterVersion = '5.8.1' 15 | testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" 16 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" 17 | 18 | testImplementation platform('org.testcontainers:testcontainers-bom:1.17.3') 19 | testImplementation 'org.testcontainers:junit-jupiter' 20 | testImplementation 'org.slf4j:slf4j-simple:1.7.32' 21 | } 22 | 23 | test { 24 | useJUnitPlatform() 25 | testLogging { 26 | showStandardStreams = true 27 | } 28 | } -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/02-postgesql-database-container/start/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | 7 | com.bmuschko 8 | postgresql-database-container 9 | 1.0.0 10 | 11 | 12 | 11 13 | UTF-8 14 | 42.2.24 15 | 5.8.1 16 | 17 | 18 | 19 | 20 | org.junit.jupiter 21 | junit-jupiter-api 22 | ${junit.jupiter.version} 23 | test 24 | 25 | 26 | org.junit.jupiter 27 | junit-jupiter-engine 28 | ${junit.jupiter.version} 29 | test 30 | 31 | 32 | org.testcontainers 33 | junit-jupiter 34 | test 35 | 36 | 37 | org.slf4j 38 | slf4j-simple 39 | 1.7.32 40 | test 41 | 42 | 43 | 44 | 45 | 46 | 47 | org.testcontainers 48 | testcontainers-bom 49 | 1.17.3 50 | pom 51 | import 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | org.apache.maven.plugins 61 | maven-compiler-plugin 62 | 3.8.1 63 | 64 | ${jdk.version} 65 | ${jdk.version} 66 | ${project.build.sourceEncoding} 67 | 68 | 69 | 70 | maven-surefire-plugin 71 | 2.22.2 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'postgresql-database-container' -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/src/main/java/com/bmuschko/testcontainers/Item.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class Item { 6 | private Long id; 7 | private String name; 8 | private BigDecimal price; 9 | 10 | public Long getId() { 11 | return id; 12 | } 13 | 14 | public void setId(Long id) { 15 | this.id = id; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | public BigDecimal getPrice() { 27 | return price; 28 | } 29 | 30 | public void setPrice(BigDecimal price) { 31 | this.price = price; 32 | } 33 | 34 | @Override 35 | public boolean equals(Object o) { 36 | if (this == o) { 37 | return true; 38 | } 39 | if (!(o instanceof Item)) { 40 | return false; 41 | } 42 | 43 | Item item = (Item) o; 44 | 45 | if (id != null ? !id.equals(item.id) : item.id != null) { 46 | return false; 47 | } 48 | if (name != null ? !name.equals(item.name) : item.name != null) { 49 | return false; 50 | } 51 | return price != null ? price.equals(item.price) : item.price == null; 52 | } 53 | 54 | @Override 55 | public int hashCode() { 56 | int result = id != null ? id.hashCode() : 0; 57 | result = 31 * result + (name != null ? name.hashCode() : 0); 58 | result = 31 * result + (price != null ? price.hashCode() : 0); 59 | return result; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/src/main/java/com/bmuschko/testcontainers/WarehouseException.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | public class WarehouseException extends RuntimeException { 4 | public WarehouseException(String message, Throwable cause) { 5 | super(message, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/src/main/java/com/bmuschko/testcontainers/WarehouseRepository.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | public interface WarehouseRepository { 4 | void insertItem(Item item); 5 | Item getItem(Long id); 6 | } 7 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/src/main/java/com/bmuschko/testcontainers/WarehouseRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import java.sql.Connection; 4 | import java.sql.DriverManager; 5 | import java.sql.PreparedStatement; 6 | import java.sql.ResultSet; 7 | import java.sql.SQLException; 8 | import java.sql.Statement; 9 | import java.util.Properties; 10 | 11 | public class WarehouseRepositoryImpl implements WarehouseRepository { 12 | private final String jdbcUrl; 13 | private final String username; 14 | private final String password; 15 | 16 | public WarehouseRepositoryImpl(String jdbcUrl, String username, String password) { 17 | this.jdbcUrl = jdbcUrl; 18 | this.username = username; 19 | this.password = password; 20 | } 21 | 22 | @Override 23 | public void insertItem(Item item) { 24 | Connection connection = null; 25 | PreparedStatement pstmt = null; 26 | ResultSet rs = null; 27 | 28 | try { 29 | connection = createConnection(); 30 | pstmt = connection.prepareStatement("INSERT INTO item (name, price) VALUES (?, ?) RETURNING ID"); 31 | pstmt.setString(1, item.getName()); 32 | pstmt.setBigDecimal(2, item.getPrice()); 33 | rs = pstmt.executeQuery(); 34 | 35 | while (rs.next()) { 36 | item.setId(rs.getLong(1)); 37 | } 38 | } catch (SQLException e) { 39 | throw new WarehouseException("Unable to insert item", e); 40 | } finally { 41 | closeResultSet(rs); 42 | closeStatement(pstmt); 43 | closeConnection(connection); 44 | } 45 | } 46 | 47 | @Override 48 | public Item getItem(Long id) { 49 | Connection connection = null; 50 | PreparedStatement pstmt = null; 51 | ResultSet rs = null; 52 | Item item = null; 53 | 54 | try { 55 | connection = createConnection(); 56 | pstmt = connection.prepareStatement("SELECT id, name, price FROM item WHERE id = ?"); 57 | pstmt.setLong(1, id); 58 | rs = pstmt.executeQuery(); 59 | 60 | while (rs.next()) { 61 | item = new Item(); 62 | item.setId(rs.getLong(1)); 63 | item.setName(rs.getString(2)); 64 | item.setPrice(rs.getBigDecimal(3)); 65 | } 66 | 67 | return item; 68 | } catch (SQLException e) { 69 | throw new WarehouseException("Unable to insert item", e); 70 | } finally { 71 | closeResultSet(rs); 72 | closeStatement(pstmt); 73 | closeConnection(connection); 74 | } 75 | } 76 | 77 | private Connection createConnection() { 78 | Properties props = new Properties(); 79 | props.setProperty("user", username); 80 | props.setProperty("password", password); 81 | 82 | try { 83 | return DriverManager.getConnection(jdbcUrl, props); 84 | } catch (SQLException e) { 85 | throw new WarehouseException("Unable to establish connection to database", e); 86 | } 87 | } 88 | 89 | private void closeConnection(Connection connection) { 90 | try { 91 | if (connection != null && !connection.isClosed()) { 92 | connection.close(); 93 | } 94 | } catch (SQLException e) { 95 | // ignore 96 | } 97 | } 98 | 99 | private void closeStatement(Statement statement) { 100 | try { 101 | if (statement != null && !statement.isClosed()) { 102 | statement.close(); 103 | } 104 | } catch (SQLException e) { 105 | // ignore 106 | } 107 | } 108 | 109 | private void closeResultSet(ResultSet resultSet) { 110 | try { 111 | if (resultSet != null && !resultSet.isClosed()) { 112 | resultSet.close(); 113 | } 114 | } catch (SQLException e) { 115 | // ignore 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/src/test/java/com/bmuschko/testcontainers/WarehouseRepositoryIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.math.BigDecimal; 7 | 8 | import static org.junit.jupiter.api.Assertions.assertEquals; 9 | 10 | public class WarehouseRepositoryIntegrationTest { 11 | private WarehouseRepository warehouseRepository; 12 | 13 | @Test 14 | public void testInsertAndGetItem() { 15 | Item clock = new Item(); 16 | clock.setName("Clock"); 17 | clock.setPrice(new BigDecimal(39.99)); 18 | 19 | warehouseRepository.insertItem(clock); 20 | 21 | Item retrieved = warehouseRepository.getItem(clock.getId()); 22 | assertEquals(clock, retrieved); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /exercises/02-postgesql-database-container/start/src/test/resources/warehouse.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS ITEM; 2 | 3 | CREATE TABLE ITEM( 4 | ID SERIAL PRIMARY KEY, 5 | NAME TEXT NOT NULL, 6 | PRICE NUMERIC NOT NULL 7 | ); -------------------------------------------------------------------------------- /exercises/03-multi-container/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 3 2 | 3 | In this exercise, you will learn how to use Testcontainers to bring up multiple containers, one for a PostgreSQL database and one for Solr. You can find the initial code in the `start` directory. The production source class under test is named `WarehouseServiceImpl.java` in the directory `src/main/java`. The test class `WarehouseServiceImplIntegrationTest.java` in the directory `src/test/java` already implements a test case. 4 | 5 | > **_NOTE:_** You can choose to run the build using Maven or Gradle. Pick the tool you are most comfortable with. 6 | 7 | 1. In the test class, create an instance of `org.testcontainers.containers.PostgreSQLContainer`. Assign the image `postgres:9.6.12` and the database name `warehouse`. Upon creation, the container instance should run the script `warehouse.sql` to create the database schema. 8 | 2. Create another instance of `org.testcontainers.containers.SolrContainer`. Assign the image `solr:8.9.0`. Call the method `createProductsCollection` to create the initial Solr collection. Make sure to add the right dependency for the container type. 9 | 3. Run the test and verify its correct behavior. For Maven the command is `./mvnw test`, for Gradle the command is `./gradlew test`. The console output should indicate that the container has been created and destroyed after the test finished. 10 | 4. Instead of creating individual container instances, use the TestContainers Docker Compose functionality instead. Create an instance of `org.testcontainers.containers.DockerComposeContainer` and point it to the YAML file `src/test/resources/warehouse-test.yml`. PostgreSQL runs on port 5432, Solr exposes its service on port 8983. 11 | 5. Run the test and verify its correct behavior. For Maven the command is `./mvnw test`, for Gradle the command is `./gradlew test`. The console output should indicate that the container has been created and destroyed after the test finished. -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/03-multi-container/solution/docker-compose/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group = 'com.bmuschko' 4 | version = '1.0.0' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_11 7 | targetCompatibility = JavaVersion.VERSION_11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | implementation 'org.apache.solr:solr-solrj:8.1.0' 15 | runtimeOnly 'org.postgresql:postgresql:42.5.0' 16 | 17 | def junitJupiterVersion = '5.8.1' 18 | testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" 19 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" 20 | 21 | testImplementation platform('org.testcontainers:testcontainers-bom:1.17.3') 22 | testImplementation 'org.testcontainers:junit-jupiter' 23 | testImplementation 'org.testcontainers:postgresql' 24 | testImplementation 'org.testcontainers:solr' 25 | testImplementation 'org.slf4j:slf4j-simple:1.7.32' 26 | } 27 | 28 | test { 29 | useJUnitPlatform() 30 | testLogging { 31 | showStandardStreams = true 32 | } 33 | } -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/03-multi-container/solution/docker-compose/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | 7 | com.bmuschko 8 | docker-compose 9 | 1.0.0 10 | 11 | 12 | 11 13 | UTF-8 14 | 8.1.0 15 | 42.5.0 16 | 5.8.1 17 | 18 | 19 | 20 | 21 | org.apache.solr 22 | solr-solrj 23 | ${solrj.version} 24 | 25 | 26 | org.postgresql 27 | postgresql 28 | ${postgresql.version} 29 | runtime 30 | 31 | 32 | org.junit.jupiter 33 | junit-jupiter-api 34 | ${junit.jupiter.version} 35 | test 36 | 37 | 38 | org.junit.jupiter 39 | junit-jupiter-engine 40 | ${junit.jupiter.version} 41 | test 42 | 43 | 44 | org.testcontainers 45 | junit-jupiter 46 | test 47 | 48 | 49 | org.testcontainers 50 | postgresql 51 | test 52 | 53 | 54 | org.testcontainers 55 | solr 56 | test 57 | 58 | 59 | org.slf4j 60 | slf4j-simple 61 | 1.7.32 62 | test 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.testcontainers 70 | testcontainers-bom 71 | 1.17.3 72 | pom 73 | import 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-compiler-plugin 84 | 3.8.1 85 | 86 | ${jdk.version} 87 | ${jdk.version} 88 | ${project.build.sourceEncoding} 89 | 90 | 91 | 92 | maven-surefire-plugin 93 | 2.22.2 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'docker-compose' -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/main/java/com/bmuschko/testcontainers/model/warehouse/Product.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.model.warehouse; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class Product { 6 | private Long id; 7 | private String name; 8 | private String category; 9 | private BigDecimal price; 10 | 11 | public Long getId() { 12 | return id; 13 | } 14 | 15 | public void setId(Long id) { 16 | this.id = id; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public void setName(String name) { 24 | this.name = name; 25 | } 26 | 27 | public String getCategory() { 28 | return category; 29 | } 30 | 31 | public void setCategory(String category) { 32 | this.category = category; 33 | } 34 | 35 | public BigDecimal getPrice() { 36 | return price; 37 | } 38 | 39 | public void setPrice(BigDecimal price) { 40 | this.price = price; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) { 46 | return true; 47 | } 48 | if (!(o instanceof Product)) { 49 | return false; 50 | } 51 | 52 | Product product = (Product) o; 53 | 54 | if (id != null ? !id.equals(product.id) : product.id != null) { 55 | return false; 56 | } 57 | if (name != null ? !name.equals(product.name) : product.name != null) { 58 | return false; 59 | } 60 | if (category != null ? !category.equals(product.category) : product.category != null) { 61 | return false; 62 | } 63 | return price != null ? price.equals(product.price) : product.price == null; 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | int result = id != null ? id.hashCode() : 0; 69 | result = 31 * result + (name != null ? name.hashCode() : 0); 70 | result = 31 * result + (category != null ? category.hashCode() : 0); 71 | result = 31 * result + (price != null ? price.hashCode() : 0); 72 | return result; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/UsernamePasswordCredentials.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | public class UsernamePasswordCredentials { 4 | private final String username; 5 | private final String password; 6 | 7 | public UsernamePasswordCredentials(String username, String password) { 8 | this.username = username; 9 | this.password = password; 10 | } 11 | 12 | public String getUsername() { 13 | return username; 14 | } 15 | 16 | public String getPassword() { 17 | return password; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/WarehouseDatabaseException.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | public class WarehouseDatabaseException extends RuntimeException { 4 | public WarehouseDatabaseException(String message, Throwable cause) { 5 | super(message, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/WarehouseDatabaseRepository.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | public interface WarehouseDatabaseRepository { 6 | void insertProduct(Product product); 7 | Product getProduct(Long id); 8 | } 9 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/WarehouseDatabaseRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | import java.sql.Connection; 6 | import java.sql.DriverManager; 7 | import java.sql.PreparedStatement; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.sql.Statement; 11 | import java.util.Properties; 12 | 13 | public class WarehouseDatabaseRepositoryImpl implements WarehouseDatabaseRepository { 14 | private final String jdbcUrl; 15 | private final UsernamePasswordCredentials credentials; 16 | 17 | public WarehouseDatabaseRepositoryImpl(String jdbcUrl, UsernamePasswordCredentials credentials) { 18 | this.jdbcUrl = jdbcUrl; 19 | this.credentials = credentials; 20 | } 21 | 22 | @Override 23 | public void insertProduct(Product product) { 24 | Connection connection = null; 25 | PreparedStatement pstmt = null; 26 | ResultSet rs = null; 27 | 28 | try { 29 | connection = createConnection(); 30 | pstmt = connection.prepareStatement("INSERT INTO item (name, price) VALUES (?, ?) RETURNING ID"); 31 | pstmt.setString(1, product.getName()); 32 | pstmt.setBigDecimal(2, product.getPrice()); 33 | rs = pstmt.executeQuery(); 34 | 35 | while (rs.next()) { 36 | product.setId(rs.getLong(1)); 37 | } 38 | } catch (SQLException e) { 39 | throw new WarehouseDatabaseException("Unable to insert item", e); 40 | } finally { 41 | closeResultSet(rs); 42 | closeStatement(pstmt); 43 | closeConnection(connection); 44 | } 45 | } 46 | 47 | @Override 48 | public Product getProduct(Long id) { 49 | Connection connection = null; 50 | PreparedStatement pstmt = null; 51 | ResultSet rs = null; 52 | Product product = null; 53 | 54 | try { 55 | connection = createConnection(); 56 | pstmt = connection.prepareStatement("SELECT id, name, price FROM item WHERE id = ?"); 57 | pstmt.setLong(1, id); 58 | rs = pstmt.executeQuery(); 59 | 60 | while (rs.next()) { 61 | product = new Product(); 62 | product.setId(rs.getLong(1)); 63 | product.setName(rs.getString(2)); 64 | product.setPrice(rs.getBigDecimal(3)); 65 | } 66 | 67 | return product; 68 | } catch (SQLException e) { 69 | throw new WarehouseDatabaseException("Unable to insert item", e); 70 | } finally { 71 | closeResultSet(rs); 72 | closeStatement(pstmt); 73 | closeConnection(connection); 74 | } 75 | } 76 | 77 | private Connection createConnection() { 78 | Properties props = new Properties(); 79 | props.setProperty("user", credentials.getUsername()); 80 | props.setProperty("password", credentials.getPassword()); 81 | 82 | try { 83 | return DriverManager.getConnection(jdbcUrl, props); 84 | } catch (SQLException e) { 85 | throw new WarehouseDatabaseException("Unable to establish connection to database", e); 86 | } 87 | } 88 | 89 | private void closeConnection(Connection connection) { 90 | try { 91 | if (connection != null && !connection.isClosed()) { 92 | connection.close(); 93 | } 94 | } catch (SQLException e) { 95 | // ignore 96 | } 97 | } 98 | 99 | private void closeStatement(Statement statement) { 100 | try { 101 | if (statement != null && !statement.isClosed()) { 102 | statement.close(); 103 | } 104 | } catch (SQLException e) { 105 | // ignore 106 | } 107 | } 108 | 109 | private void closeResultSet(ResultSet resultSet) { 110 | try { 111 | if (resultSet != null && !resultSet.isClosed()) { 112 | resultSet.close(); 113 | } 114 | } catch (SQLException e) { 115 | // ignore 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/main/java/com/bmuschko/testcontainers/repository/warehouse/index/ProductIndexException.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.index; 2 | 3 | public class ProductIndexException extends RuntimeException { 4 | public ProductIndexException(String message, Throwable cause) { 5 | super(message, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/main/java/com/bmuschko/testcontainers/repository/warehouse/index/ProductIndexRepository.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.index; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | public interface ProductIndexRepository { 6 | void insertProduct(Product product); 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/main/java/com/bmuschko/testcontainers/repository/warehouse/index/ProductIndexRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.index; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | import org.apache.solr.client.solrj.SolrClient; 5 | import org.apache.solr.client.solrj.SolrServerException; 6 | import org.apache.solr.client.solrj.impl.HttpSolrClient; 7 | import org.apache.solr.common.SolrInputDocument; 8 | 9 | import java.io.IOException; 10 | 11 | public class ProductIndexRepositoryImpl implements ProductIndexRepository { 12 | public static final String PRODUCTS_COLLECTION = "products"; 13 | private final String serverUrl; 14 | 15 | public ProductIndexRepositoryImpl(String serverUrl) { 16 | this.serverUrl = serverUrl; 17 | } 18 | 19 | @Override 20 | public void insertProduct(Product product) { 21 | SolrClient client = null; 22 | 23 | try { 24 | client = createSolrClient(); 25 | SolrInputDocument doc = new SolrInputDocument() ; 26 | doc.addField("name", product.getName()); 27 | doc.addField("category", product.getCategory()); 28 | client.add(PRODUCTS_COLLECTION, doc); 29 | client.commit(PRODUCTS_COLLECTION); 30 | } catch (IOException | SolrServerException e) { 31 | throw new ProductIndexException("Failed to insert product", e); 32 | } finally { 33 | closeCloseable(client); 34 | } 35 | } 36 | 37 | private SolrClient createSolrClient() { 38 | return new HttpSolrClient.Builder(serverUrl) 39 | .withConnectionTimeout(10000) 40 | .withSocketTimeout(60000) 41 | .build(); 42 | } 43 | 44 | private void closeCloseable(SolrClient client) { 45 | try { 46 | if (client != null) { 47 | client.close(); 48 | } 49 | } catch (IOException e) { 50 | // ignore 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/main/java/com/bmuschko/testcontainers/service/WarehouseService.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.service; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | public interface WarehouseService { 6 | void addProduct(Product product); 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/main/java/com/bmuschko/testcontainers/service/WarehouseServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.service; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | import com.bmuschko.testcontainers.repository.warehouse.db.UsernamePasswordCredentials; 5 | import com.bmuschko.testcontainers.repository.warehouse.db.WarehouseDatabaseRepository; 6 | import com.bmuschko.testcontainers.repository.warehouse.db.WarehouseDatabaseRepositoryImpl; 7 | import com.bmuschko.testcontainers.repository.warehouse.index.ProductIndexRepository; 8 | import com.bmuschko.testcontainers.repository.warehouse.index.ProductIndexRepositoryImpl; 9 | 10 | public class WarehouseServiceImpl implements WarehouseService { 11 | private final WarehouseDatabaseRepository warehouseDatabaseRepository; 12 | private final ProductIndexRepository productIndexRepository; 13 | 14 | public WarehouseServiceImpl(String databaseUrl, UsernamePasswordCredentials databaseCredentials, String solrUrl) { 15 | warehouseDatabaseRepository = new WarehouseDatabaseRepositoryImpl(databaseUrl, databaseCredentials); 16 | productIndexRepository = new ProductIndexRepositoryImpl(solrUrl); 17 | } 18 | 19 | @Override 20 | public void addProduct(Product product) { 21 | warehouseDatabaseRepository.insertProduct(product); 22 | productIndexRepository.insertProduct(product); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/test/java/com/bmuschko/testcontainers/service/WarehouseServiceImplIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.service; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | import com.bmuschko.testcontainers.repository.warehouse.db.UsernamePasswordCredentials; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import org.testcontainers.containers.DockerComposeContainer; 8 | import org.testcontainers.junit.jupiter.Container; 9 | import org.testcontainers.junit.jupiter.Testcontainers; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.math.BigDecimal; 14 | import java.net.URI; 15 | import java.net.http.HttpClient; 16 | import java.net.http.HttpRequest; 17 | import java.net.http.HttpResponse; 18 | 19 | @Testcontainers 20 | public class WarehouseServiceImplIntegrationTest { 21 | private static final String POSTGRESQL_SERVICE_NAME = "postgresql_1"; 22 | private static final int POSTGRESQL_PORT = 5432; 23 | private static final String SOLR_SERVICE_NAME = "solr_1"; 24 | private static final int SOLR_PORT = 8983; 25 | private WarehouseService warehouseService; 26 | 27 | @Container 28 | private final DockerComposeContainer environment = new DockerComposeContainer(new File("src/test/resources/warehouse-test.yml")) 29 | .withExposedService(POSTGRESQL_SERVICE_NAME, POSTGRESQL_PORT) 30 | .withExposedService(SOLR_SERVICE_NAME, SOLR_PORT); 31 | 32 | @BeforeEach 33 | public void setUp() { 34 | UsernamePasswordCredentials postgreSqlCredentials = new UsernamePasswordCredentials("postgres", "postgres"); 35 | String solrUrl = createSolrUrl(); 36 | createProductsCollection(solrUrl); 37 | warehouseService = new WarehouseServiceImpl(createPostgreSqlUrl(), postgreSqlCredentials, solrUrl); 38 | } 39 | 40 | @Test 41 | public void testInsertProduct() { 42 | Product product = new Product(); 43 | product.setName("Clock"); 44 | product.setPrice(new BigDecimal(39.99)); 45 | product.setCategory("Household"); 46 | warehouseService.addProduct(product); 47 | } 48 | 49 | private String createPostgreSqlUrl() { 50 | return "jdbc:postgresql://" + environment.getServiceHost(POSTGRESQL_SERVICE_NAME, POSTGRESQL_PORT) + ":" + environment.getServicePort(POSTGRESQL_SERVICE_NAME, POSTGRESQL_PORT) + "/warehouse"; 51 | } 52 | 53 | private String createSolrUrl() { 54 | return "http://" + environment.getServiceHost(SOLR_SERVICE_NAME, SOLR_PORT) + ":" + environment.getServicePort(SOLR_SERVICE_NAME, SOLR_PORT) + "/solr"; 55 | } 56 | 57 | private void createProductsCollection(String solrBaseUrl) { 58 | String createCollectionURL = solrBaseUrl + "/admin/collections?action=CREATE&name=products&numShards=1"; 59 | 60 | try { 61 | HttpClient client = HttpClient.newHttpClient(); 62 | HttpRequest request = HttpRequest.newBuilder(URI.create(createCollectionURL)).build(); 63 | HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); 64 | 65 | if (response.statusCode() != 200) { 66 | throw new IllegalStateException("Cannot set up products collection in Solr: " + new String(response.body().getBytes())); 67 | } 68 | } catch (IOException | InterruptedException e) { 69 | throw new IllegalStateException("Cannot set up products collection in Solr"); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/test/resources/warehouse-test.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | postgresql: 5 | image: postgres:9.6.12 6 | environment: 7 | - POSTGRES_DB=warehouse 8 | - POSTGRES_USER=postgres 9 | - POSTGRES_PASSWORD=postgres 10 | volumes: 11 | - ./warehouse.sql:/docker-entrypoint-initdb.d/warehouse.sql 12 | 13 | solr: 14 | image: solr:8.9.0 15 | command: 16 | - bash 17 | - "-c" 18 | - "solr -f -cloud" -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/docker-compose/src/test/resources/warehouse.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS ITEM; 2 | 3 | CREATE TABLE ITEM( 4 | ID SERIAL PRIMARY KEY, 5 | NAME TEXT NOT NULL, 6 | PRICE NUMERIC NOT NULL 7 | ); -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/03-multi-container/solution/multi-container/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group = 'com.bmuschko' 4 | version = '1.0.0' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_11 7 | targetCompatibility = JavaVersion.VERSION_11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | implementation 'org.apache.solr:solr-solrj:8.1.0' 15 | runtimeOnly 'org.postgresql:postgresql:42.5.0' 16 | 17 | def junitJupiterVersion = '5.8.1' 18 | testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" 19 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" 20 | 21 | testImplementation platform('org.testcontainers:testcontainers-bom:1.17.3') 22 | testImplementation 'org.testcontainers:junit-jupiter' 23 | testImplementation 'org.testcontainers:postgresql' 24 | testImplementation 'org.testcontainers:solr' 25 | testImplementation 'org.slf4j:slf4j-simple:1.7.32' 26 | } 27 | 28 | test { 29 | useJUnitPlatform() 30 | testLogging { 31 | showStandardStreams = true 32 | } 33 | } -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/03-multi-container/solution/multi-container/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | 7 | com.bmuschko 8 | multi-container 9 | 1.0.0 10 | 11 | 12 | 11 13 | UTF-8 14 | 8.1.0 15 | 42.5.0 16 | 5.8.1 17 | 18 | 19 | 20 | 21 | org.apache.solr 22 | solr-solrj 23 | ${solrj.version} 24 | 25 | 26 | org.postgresql 27 | postgresql 28 | ${postgresql.version} 29 | runtime 30 | 31 | 32 | org.junit.jupiter 33 | junit-jupiter-api 34 | ${junit.jupiter.version} 35 | test 36 | 37 | 38 | org.junit.jupiter 39 | junit-jupiter-engine 40 | ${junit.jupiter.version} 41 | test 42 | 43 | 44 | org.testcontainers 45 | junit-jupiter 46 | test 47 | 48 | 49 | org.testcontainers 50 | postgresql 51 | test 52 | 53 | 54 | org.testcontainers 55 | solr 56 | test 57 | 58 | 59 | org.slf4j 60 | slf4j-simple 61 | 1.7.32 62 | test 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.testcontainers 70 | testcontainers-bom 71 | 1.17.3 72 | pom 73 | import 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-compiler-plugin 84 | 3.8.1 85 | 86 | ${jdk.version} 87 | ${jdk.version} 88 | ${project.build.sourceEncoding} 89 | 90 | 91 | 92 | maven-surefire-plugin 93 | 2.22.2 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'multi-container' -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/main/java/com/bmuschko/testcontainers/model/warehouse/Product.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.model.warehouse; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class Product { 6 | private Long id; 7 | private String name; 8 | private String category; 9 | private BigDecimal price; 10 | 11 | public Long getId() { 12 | return id; 13 | } 14 | 15 | public void setId(Long id) { 16 | this.id = id; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public void setName(String name) { 24 | this.name = name; 25 | } 26 | 27 | public String getCategory() { 28 | return category; 29 | } 30 | 31 | public void setCategory(String category) { 32 | this.category = category; 33 | } 34 | 35 | public BigDecimal getPrice() { 36 | return price; 37 | } 38 | 39 | public void setPrice(BigDecimal price) { 40 | this.price = price; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) { 46 | return true; 47 | } 48 | if (!(o instanceof Product)) { 49 | return false; 50 | } 51 | 52 | Product product = (Product) o; 53 | 54 | if (id != null ? !id.equals(product.id) : product.id != null) { 55 | return false; 56 | } 57 | if (name != null ? !name.equals(product.name) : product.name != null) { 58 | return false; 59 | } 60 | if (category != null ? !category.equals(product.category) : product.category != null) { 61 | return false; 62 | } 63 | return price != null ? price.equals(product.price) : product.price == null; 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | int result = id != null ? id.hashCode() : 0; 69 | result = 31 * result + (name != null ? name.hashCode() : 0); 70 | result = 31 * result + (category != null ? category.hashCode() : 0); 71 | result = 31 * result + (price != null ? price.hashCode() : 0); 72 | return result; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/UsernamePasswordCredentials.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | public class UsernamePasswordCredentials { 4 | private final String username; 5 | private final String password; 6 | 7 | public UsernamePasswordCredentials(String username, String password) { 8 | this.username = username; 9 | this.password = password; 10 | } 11 | 12 | public String getUsername() { 13 | return username; 14 | } 15 | 16 | public String getPassword() { 17 | return password; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/WarehouseDatabaseException.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | public class WarehouseDatabaseException extends RuntimeException { 4 | public WarehouseDatabaseException(String message, Throwable cause) { 5 | super(message, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/WarehouseDatabaseRepository.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | public interface WarehouseDatabaseRepository { 6 | void insertProduct(Product product); 7 | Product getProduct(Long id); 8 | } 9 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/WarehouseDatabaseRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | import java.sql.Connection; 6 | import java.sql.DriverManager; 7 | import java.sql.PreparedStatement; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.sql.Statement; 11 | import java.util.Properties; 12 | 13 | public class WarehouseDatabaseRepositoryImpl implements WarehouseDatabaseRepository { 14 | private final String jdbcUrl; 15 | private final UsernamePasswordCredentials credentials; 16 | 17 | public WarehouseDatabaseRepositoryImpl(String jdbcUrl, UsernamePasswordCredentials credentials) { 18 | this.jdbcUrl = jdbcUrl; 19 | this.credentials = credentials; 20 | } 21 | 22 | @Override 23 | public void insertProduct(Product product) { 24 | Connection connection = null; 25 | PreparedStatement pstmt = null; 26 | ResultSet rs = null; 27 | 28 | try { 29 | connection = createConnection(); 30 | pstmt = connection.prepareStatement("INSERT INTO item (name, price) VALUES (?, ?) RETURNING ID"); 31 | pstmt.setString(1, product.getName()); 32 | pstmt.setBigDecimal(2, product.getPrice()); 33 | rs = pstmt.executeQuery(); 34 | 35 | while (rs.next()) { 36 | product.setId(rs.getLong(1)); 37 | } 38 | } catch (SQLException e) { 39 | throw new WarehouseDatabaseException("Unable to insert item", e); 40 | } finally { 41 | closeResultSet(rs); 42 | closeStatement(pstmt); 43 | closeConnection(connection); 44 | } 45 | } 46 | 47 | @Override 48 | public Product getProduct(Long id) { 49 | Connection connection = null; 50 | PreparedStatement pstmt = null; 51 | ResultSet rs = null; 52 | Product product = null; 53 | 54 | try { 55 | connection = createConnection(); 56 | pstmt = connection.prepareStatement("SELECT id, name, price FROM item WHERE id = ?"); 57 | pstmt.setLong(1, id); 58 | rs = pstmt.executeQuery(); 59 | 60 | while (rs.next()) { 61 | product = new Product(); 62 | product.setId(rs.getLong(1)); 63 | product.setName(rs.getString(2)); 64 | product.setPrice(rs.getBigDecimal(3)); 65 | } 66 | 67 | return product; 68 | } catch (SQLException e) { 69 | throw new WarehouseDatabaseException("Unable to insert item", e); 70 | } finally { 71 | closeResultSet(rs); 72 | closeStatement(pstmt); 73 | closeConnection(connection); 74 | } 75 | } 76 | 77 | private Connection createConnection() { 78 | Properties props = new Properties(); 79 | props.setProperty("user", credentials.getUsername()); 80 | props.setProperty("password", credentials.getPassword()); 81 | 82 | try { 83 | return DriverManager.getConnection(jdbcUrl, props); 84 | } catch (SQLException e) { 85 | throw new WarehouseDatabaseException("Unable to establish connection to database", e); 86 | } 87 | } 88 | 89 | private void closeConnection(Connection connection) { 90 | try { 91 | if (connection != null && !connection.isClosed()) { 92 | connection.close(); 93 | } 94 | } catch (SQLException e) { 95 | // ignore 96 | } 97 | } 98 | 99 | private void closeStatement(Statement statement) { 100 | try { 101 | if (statement != null && !statement.isClosed()) { 102 | statement.close(); 103 | } 104 | } catch (SQLException e) { 105 | // ignore 106 | } 107 | } 108 | 109 | private void closeResultSet(ResultSet resultSet) { 110 | try { 111 | if (resultSet != null && !resultSet.isClosed()) { 112 | resultSet.close(); 113 | } 114 | } catch (SQLException e) { 115 | // ignore 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/main/java/com/bmuschko/testcontainers/repository/warehouse/index/ProductIndexException.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.index; 2 | 3 | public class ProductIndexException extends RuntimeException { 4 | public ProductIndexException(String message, Throwable cause) { 5 | super(message, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/main/java/com/bmuschko/testcontainers/repository/warehouse/index/ProductIndexRepository.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.index; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | public interface ProductIndexRepository { 6 | void insertProduct(Product product); 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/main/java/com/bmuschko/testcontainers/repository/warehouse/index/ProductIndexRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.index; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | import org.apache.solr.client.solrj.SolrClient; 5 | import org.apache.solr.client.solrj.SolrServerException; 6 | import org.apache.solr.client.solrj.impl.HttpSolrClient; 7 | import org.apache.solr.common.SolrInputDocument; 8 | 9 | import java.io.IOException; 10 | 11 | public class ProductIndexRepositoryImpl implements ProductIndexRepository { 12 | public static final String PRODUCTS_COLLECTION = "products"; 13 | private final String serverUrl; 14 | 15 | public ProductIndexRepositoryImpl(String serverUrl) { 16 | this.serverUrl = serverUrl; 17 | } 18 | 19 | @Override 20 | public void insertProduct(Product product) { 21 | SolrClient client = null; 22 | 23 | try { 24 | client = createSolrClient(); 25 | SolrInputDocument doc = new SolrInputDocument() ; 26 | doc.addField("name", product.getName()); 27 | doc.addField("category", product.getCategory()); 28 | client.add(PRODUCTS_COLLECTION, doc); 29 | client.commit(PRODUCTS_COLLECTION); 30 | } catch (IOException | SolrServerException e) { 31 | throw new ProductIndexException("Failed to insert product", e); 32 | } finally { 33 | closeCloseable(client); 34 | } 35 | } 36 | 37 | private SolrClient createSolrClient() { 38 | return new HttpSolrClient.Builder(serverUrl) 39 | .withConnectionTimeout(10000) 40 | .withSocketTimeout(60000) 41 | .build(); 42 | } 43 | 44 | private void closeCloseable(SolrClient client) { 45 | try { 46 | if (client != null) { 47 | client.close(); 48 | } 49 | } catch (IOException e) { 50 | // ignore 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/main/java/com/bmuschko/testcontainers/service/WarehouseService.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.service; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | public interface WarehouseService { 6 | void addProduct(Product product); 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/main/java/com/bmuschko/testcontainers/service/WarehouseServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.service; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | import com.bmuschko.testcontainers.repository.warehouse.db.UsernamePasswordCredentials; 5 | import com.bmuschko.testcontainers.repository.warehouse.db.WarehouseDatabaseRepository; 6 | import com.bmuschko.testcontainers.repository.warehouse.db.WarehouseDatabaseRepositoryImpl; 7 | import com.bmuschko.testcontainers.repository.warehouse.index.ProductIndexRepository; 8 | import com.bmuschko.testcontainers.repository.warehouse.index.ProductIndexRepositoryImpl; 9 | 10 | public class WarehouseServiceImpl implements WarehouseService { 11 | private final WarehouseDatabaseRepository warehouseDatabaseRepository; 12 | private final ProductIndexRepository productIndexRepository; 13 | 14 | public WarehouseServiceImpl(String databaseUrl, UsernamePasswordCredentials databaseCredentials, String solrUrl) { 15 | warehouseDatabaseRepository = new WarehouseDatabaseRepositoryImpl(databaseUrl, databaseCredentials); 16 | productIndexRepository = new ProductIndexRepositoryImpl(solrUrl); 17 | } 18 | 19 | @Override 20 | public void addProduct(Product product) { 21 | warehouseDatabaseRepository.insertProduct(product); 22 | productIndexRepository.insertProduct(product); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/test/java/com/bmuschko/testcontainers/service/WarehouseServiceImplIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.service; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | import com.bmuschko.testcontainers.repository.warehouse.db.UsernamePasswordCredentials; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import org.testcontainers.containers.JdbcDatabaseContainer; 8 | import org.testcontainers.containers.PostgreSQLContainer; 9 | import org.testcontainers.containers.SolrContainer; 10 | import org.testcontainers.junit.jupiter.Container; 11 | import org.testcontainers.junit.jupiter.Testcontainers; 12 | import org.testcontainers.utility.DockerImageName; 13 | 14 | import java.io.IOException; 15 | import java.math.BigDecimal; 16 | import java.net.URI; 17 | import java.net.http.HttpClient; 18 | import java.net.http.HttpRequest; 19 | import java.net.http.HttpResponse; 20 | 21 | @Testcontainers 22 | public class WarehouseServiceImplIntegrationTest { 23 | private WarehouseService warehouseService; 24 | 25 | @Container 26 | private final JdbcDatabaseContainer postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.12") 27 | .withInitScript("warehouse.sql") 28 | .withDatabaseName("warehouse"); 29 | 30 | @Container 31 | private final SolrContainer solrContainer = new SolrContainer(DockerImageName.parse("solr:8.9.0")); 32 | 33 | @BeforeEach 34 | public void setUp() { 35 | UsernamePasswordCredentials postgreSqlCredentials = new UsernamePasswordCredentials(postgreSQLContainer.getUsername(), postgreSQLContainer.getPassword()); 36 | String solrUrl = createSolrUrl(); 37 | createProductsCollection(solrUrl); 38 | warehouseService = new WarehouseServiceImpl(postgreSQLContainer.getJdbcUrl(), postgreSqlCredentials, solrUrl); 39 | } 40 | 41 | @Test 42 | public void testInsertProduct() { 43 | Product product = new Product(); 44 | product.setName("Clock"); 45 | product.setPrice(new BigDecimal(39.99)); 46 | product.setCategory("Household"); 47 | warehouseService.addProduct(product); 48 | } 49 | 50 | private String createSolrUrl() { 51 | return "http://" + solrContainer.getContainerIpAddress() + ":" + solrContainer.getSolrPort() + "/solr"; 52 | } 53 | 54 | private void createProductsCollection(String solrBaseUrl) { 55 | String createCollectionURL = solrBaseUrl + "/admin/collections?action=CREATE&name=products&numShards=1"; 56 | 57 | try { 58 | HttpClient client = HttpClient.newHttpClient(); 59 | HttpRequest request = HttpRequest.newBuilder(URI.create(createCollectionURL)).build(); 60 | HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); 61 | 62 | if (response.statusCode() != 200) { 63 | throw new IllegalStateException("Cannot set up products collection in Solr: " + new String(response.body().getBytes())); 64 | } 65 | } catch (IOException | InterruptedException e) { 66 | throw new IllegalStateException("Cannot set up products collection in Solr"); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /exercises/03-multi-container/solution/multi-container/src/test/resources/warehouse.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS ITEM; 2 | 3 | CREATE TABLE ITEM( 4 | ID SERIAL PRIMARY KEY, 5 | NAME TEXT NOT NULL, 6 | PRICE NUMERIC NOT NULL 7 | ); -------------------------------------------------------------------------------- /exercises/03-multi-container/start/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/03-multi-container/start/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exercises/03-multi-container/start/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group = 'com.bmuschko' 4 | version = '1.0.0' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_11 7 | targetCompatibility = JavaVersion.VERSION_11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | implementation 'org.apache.solr:solr-solrj:8.1.0' 15 | runtimeOnly 'org.postgresql:postgresql:42.5.0' 16 | 17 | def junitJupiterVersion = '5.8.1' 18 | testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" 19 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" 20 | 21 | testImplementation platform('org.testcontainers:testcontainers-bom:1.17.3') 22 | testImplementation 'org.testcontainers:junit-jupiter' 23 | testImplementation 'org.testcontainers:postgresql' 24 | testImplementation 'org.testcontainers:solr' 25 | testImplementation 'org.slf4j:slf4j-simple:1.7.32' 26 | } 27 | 28 | test { 29 | useJUnitPlatform() 30 | testLogging { 31 | showStandardStreams = true 32 | } 33 | } -------------------------------------------------------------------------------- /exercises/03-multi-container/start/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/03-multi-container/start/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exercises/03-multi-container/start/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | 7 | com.bmuschko 8 | multi-container 9 | 1.0.0 10 | 11 | 12 | 11 13 | UTF-8 14 | 8.1.0 15 | 42.5.0 16 | 5.8.1 17 | 18 | 19 | 20 | 21 | org.apache.solr 22 | solr-solrj 23 | ${solrj.version} 24 | 25 | 26 | org.postgresql 27 | postgresql 28 | ${postgresql.version} 29 | runtime 30 | 31 | 32 | org.junit.jupiter 33 | junit-jupiter-api 34 | ${junit.jupiter.version} 35 | test 36 | 37 | 38 | org.junit.jupiter 39 | junit-jupiter-engine 40 | ${junit.jupiter.version} 41 | test 42 | 43 | 44 | org.testcontainers 45 | junit-jupiter 46 | test 47 | 48 | 49 | org.testcontainers 50 | postgresql 51 | test 52 | 53 | 54 | org.testcontainers 55 | solr 56 | test 57 | 58 | 59 | org.slf4j 60 | slf4j-simple 61 | 1.7.32 62 | test 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.testcontainers 70 | testcontainers-bom 71 | 1.17.3 72 | pom 73 | import 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-compiler-plugin 84 | 3.8.1 85 | 86 | ${jdk.version} 87 | ${jdk.version} 88 | ${project.build.sourceEncoding} 89 | 90 | 91 | 92 | maven-surefire-plugin 93 | 2.22.2 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'multi-container' -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/main/java/com/bmuschko/testcontainers/model/warehouse/Product.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.model.warehouse; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class Product { 6 | private Long id; 7 | private String name; 8 | private String category; 9 | private BigDecimal price; 10 | 11 | public Long getId() { 12 | return id; 13 | } 14 | 15 | public void setId(Long id) { 16 | this.id = id; 17 | } 18 | 19 | public String getName() { 20 | return name; 21 | } 22 | 23 | public void setName(String name) { 24 | this.name = name; 25 | } 26 | 27 | public String getCategory() { 28 | return category; 29 | } 30 | 31 | public void setCategory(String category) { 32 | this.category = category; 33 | } 34 | 35 | public BigDecimal getPrice() { 36 | return price; 37 | } 38 | 39 | public void setPrice(BigDecimal price) { 40 | this.price = price; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) { 46 | return true; 47 | } 48 | if (!(o instanceof Product)) { 49 | return false; 50 | } 51 | 52 | Product product = (Product) o; 53 | 54 | if (id != null ? !id.equals(product.id) : product.id != null) { 55 | return false; 56 | } 57 | if (name != null ? !name.equals(product.name) : product.name != null) { 58 | return false; 59 | } 60 | if (category != null ? !category.equals(product.category) : product.category != null) { 61 | return false; 62 | } 63 | return price != null ? price.equals(product.price) : product.price == null; 64 | } 65 | 66 | @Override 67 | public int hashCode() { 68 | int result = id != null ? id.hashCode() : 0; 69 | result = 31 * result + (name != null ? name.hashCode() : 0); 70 | result = 31 * result + (category != null ? category.hashCode() : 0); 71 | result = 31 * result + (price != null ? price.hashCode() : 0); 72 | return result; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/UsernamePasswordCredentials.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | public class UsernamePasswordCredentials { 4 | private final String username; 5 | private final String password; 6 | 7 | public UsernamePasswordCredentials(String username, String password) { 8 | this.username = username; 9 | this.password = password; 10 | } 11 | 12 | public String getUsername() { 13 | return username; 14 | } 15 | 16 | public String getPassword() { 17 | return password; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/WarehouseDatabaseException.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | public class WarehouseDatabaseException extends RuntimeException { 4 | public WarehouseDatabaseException(String message, Throwable cause) { 5 | super(message, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/WarehouseDatabaseRepository.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | public interface WarehouseDatabaseRepository { 6 | void insertProduct(Product product); 7 | Product getProduct(Long id); 8 | } 9 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/main/java/com/bmuschko/testcontainers/repository/warehouse/db/WarehouseDatabaseRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.db; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | import java.sql.Connection; 6 | import java.sql.DriverManager; 7 | import java.sql.PreparedStatement; 8 | import java.sql.ResultSet; 9 | import java.sql.SQLException; 10 | import java.sql.Statement; 11 | import java.util.Properties; 12 | 13 | public class WarehouseDatabaseRepositoryImpl implements WarehouseDatabaseRepository { 14 | private final String jdbcUrl; 15 | private final UsernamePasswordCredentials credentials; 16 | 17 | public WarehouseDatabaseRepositoryImpl(String jdbcUrl, UsernamePasswordCredentials credentials) { 18 | this.jdbcUrl = jdbcUrl; 19 | this.credentials = credentials; 20 | } 21 | 22 | @Override 23 | public void insertProduct(Product product) { 24 | Connection connection = null; 25 | PreparedStatement pstmt = null; 26 | ResultSet rs = null; 27 | 28 | try { 29 | connection = createConnection(); 30 | pstmt = connection.prepareStatement("INSERT INTO item (name, price) VALUES (?, ?) RETURNING ID"); 31 | pstmt.setString(1, product.getName()); 32 | pstmt.setBigDecimal(2, product.getPrice()); 33 | rs = pstmt.executeQuery(); 34 | 35 | while (rs.next()) { 36 | product.setId(rs.getLong(1)); 37 | } 38 | } catch (SQLException e) { 39 | throw new WarehouseDatabaseException("Unable to insert item", e); 40 | } finally { 41 | closeResultSet(rs); 42 | closeStatement(pstmt); 43 | closeConnection(connection); 44 | } 45 | } 46 | 47 | @Override 48 | public Product getProduct(Long id) { 49 | Connection connection = null; 50 | PreparedStatement pstmt = null; 51 | ResultSet rs = null; 52 | Product product = null; 53 | 54 | try { 55 | connection = createConnection(); 56 | pstmt = connection.prepareStatement("SELECT id, name, price FROM item WHERE id = ?"); 57 | pstmt.setLong(1, id); 58 | rs = pstmt.executeQuery(); 59 | 60 | while (rs.next()) { 61 | product = new Product(); 62 | product.setId(rs.getLong(1)); 63 | product.setName(rs.getString(2)); 64 | product.setPrice(rs.getBigDecimal(3)); 65 | } 66 | 67 | return product; 68 | } catch (SQLException e) { 69 | throw new WarehouseDatabaseException("Unable to insert item", e); 70 | } finally { 71 | closeResultSet(rs); 72 | closeStatement(pstmt); 73 | closeConnection(connection); 74 | } 75 | } 76 | 77 | private Connection createConnection() { 78 | Properties props = new Properties(); 79 | props.setProperty("user", credentials.getUsername()); 80 | props.setProperty("password", credentials.getPassword()); 81 | 82 | try { 83 | return DriverManager.getConnection(jdbcUrl, props); 84 | } catch (SQLException e) { 85 | throw new WarehouseDatabaseException("Unable to establish connection to database", e); 86 | } 87 | } 88 | 89 | private void closeConnection(Connection connection) { 90 | try { 91 | if (connection != null && !connection.isClosed()) { 92 | connection.close(); 93 | } 94 | } catch (SQLException e) { 95 | // ignore 96 | } 97 | } 98 | 99 | private void closeStatement(Statement statement) { 100 | try { 101 | if (statement != null && !statement.isClosed()) { 102 | statement.close(); 103 | } 104 | } catch (SQLException e) { 105 | // ignore 106 | } 107 | } 108 | 109 | private void closeResultSet(ResultSet resultSet) { 110 | try { 111 | if (resultSet != null && !resultSet.isClosed()) { 112 | resultSet.close(); 113 | } 114 | } catch (SQLException e) { 115 | // ignore 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/main/java/com/bmuschko/testcontainers/repository/warehouse/index/ProductIndexException.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.index; 2 | 3 | public class ProductIndexException extends RuntimeException { 4 | public ProductIndexException(String message, Throwable cause) { 5 | super(message, cause); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/main/java/com/bmuschko/testcontainers/repository/warehouse/index/ProductIndexRepository.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.index; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | public interface ProductIndexRepository { 6 | void insertProduct(Product product); 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/main/java/com/bmuschko/testcontainers/repository/warehouse/index/ProductIndexRepositoryImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.repository.warehouse.index; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | import org.apache.solr.client.solrj.SolrClient; 5 | import org.apache.solr.client.solrj.SolrServerException; 6 | import org.apache.solr.client.solrj.impl.HttpSolrClient; 7 | import org.apache.solr.common.SolrInputDocument; 8 | 9 | import java.io.IOException; 10 | 11 | public class ProductIndexRepositoryImpl implements ProductIndexRepository { 12 | public static final String PRODUCTS_COLLECTION = "products"; 13 | private final String serverUrl; 14 | 15 | public ProductIndexRepositoryImpl(String serverUrl) { 16 | this.serverUrl = serverUrl; 17 | } 18 | 19 | @Override 20 | public void insertProduct(Product product) { 21 | SolrClient client = null; 22 | 23 | try { 24 | client = createSolrClient(); 25 | SolrInputDocument doc = new SolrInputDocument() ; 26 | doc.addField("name", product.getName()); 27 | doc.addField("category", product.getCategory()); 28 | client.add(PRODUCTS_COLLECTION, doc); 29 | client.commit(PRODUCTS_COLLECTION); 30 | } catch (IOException | SolrServerException e) { 31 | throw new ProductIndexException("Failed to insert product", e); 32 | } finally { 33 | closeCloseable(client); 34 | } 35 | } 36 | 37 | private SolrClient createSolrClient() { 38 | return new HttpSolrClient.Builder(serverUrl) 39 | .withConnectionTimeout(10000) 40 | .withSocketTimeout(60000) 41 | .build(); 42 | } 43 | 44 | private void closeCloseable(SolrClient client) { 45 | try { 46 | if (client != null) { 47 | client.close(); 48 | } 49 | } catch (IOException e) { 50 | // ignore 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/main/java/com/bmuschko/testcontainers/service/WarehouseService.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.service; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | 5 | public interface WarehouseService { 6 | void addProduct(Product product); 7 | } 8 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/main/java/com/bmuschko/testcontainers/service/WarehouseServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.service; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | import com.bmuschko.testcontainers.repository.warehouse.db.UsernamePasswordCredentials; 5 | import com.bmuschko.testcontainers.repository.warehouse.db.WarehouseDatabaseRepository; 6 | import com.bmuschko.testcontainers.repository.warehouse.db.WarehouseDatabaseRepositoryImpl; 7 | import com.bmuschko.testcontainers.repository.warehouse.index.ProductIndexRepository; 8 | import com.bmuschko.testcontainers.repository.warehouse.index.ProductIndexRepositoryImpl; 9 | 10 | public class WarehouseServiceImpl implements WarehouseService { 11 | private final WarehouseDatabaseRepository warehouseDatabaseRepository; 12 | private final ProductIndexRepository productIndexRepository; 13 | 14 | public WarehouseServiceImpl(String databaseUrl, UsernamePasswordCredentials databaseCredentials, String solrUrl) { 15 | warehouseDatabaseRepository = new WarehouseDatabaseRepositoryImpl(databaseUrl, databaseCredentials); 16 | productIndexRepository = new ProductIndexRepositoryImpl(solrUrl); 17 | } 18 | 19 | @Override 20 | public void addProduct(Product product) { 21 | warehouseDatabaseRepository.insertProduct(product); 22 | productIndexRepository.insertProduct(product); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/test/java/com/bmuschko/testcontainers/service/WarehouseServiceImplIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers.service; 2 | 3 | import com.bmuschko.testcontainers.model.warehouse.Product; 4 | import com.bmuschko.testcontainers.repository.warehouse.db.UsernamePasswordCredentials; 5 | import org.junit.jupiter.api.BeforeEach; 6 | import org.junit.jupiter.api.Test; 7 | import org.testcontainers.junit.jupiter.Container; 8 | import org.testcontainers.junit.jupiter.Testcontainers; 9 | 10 | import java.io.File; 11 | import java.io.IOException; 12 | import java.math.BigDecimal; 13 | import java.net.URI; 14 | import java.net.http.HttpClient; 15 | import java.net.http.HttpRequest; 16 | import java.net.http.HttpResponse; 17 | 18 | @Testcontainers 19 | public class WarehouseServiceImplIntegrationTest { 20 | private WarehouseService warehouseService; 21 | 22 | @Container 23 | // Create container 24 | 25 | @BeforeEach 26 | public void setUp() { 27 | UsernamePasswordCredentials postgreSqlCredentials = new UsernamePasswordCredentials("postgres", "postgres"); 28 | String solrUrl = createSolrUrl(); 29 | createProductsCollection(solrUrl); 30 | warehouseService = new WarehouseServiceImpl(createPostgreSqlUrl(), postgreSqlCredentials, solrUrl); 31 | } 32 | 33 | @Test 34 | public void testInsertProduct() { 35 | Product product = new Product(); 36 | product.setName("Clock"); 37 | product.setPrice(new BigDecimal(39.99)); 38 | product.setCategory("Household"); 39 | warehouseService.addProduct(product); 40 | } 41 | 42 | private String createPostgreSqlUrl() { 43 | return null; 44 | } 45 | 46 | private String createSolrUrl() { 47 | return null; 48 | } 49 | 50 | private void createProductsCollection(String solrBaseUrl) { 51 | String createCollectionURL = solrBaseUrl + "/admin/collections?action=CREATE&name=products&numShards=1"; 52 | 53 | try { 54 | HttpClient client = HttpClient.newHttpClient(); 55 | HttpRequest request = HttpRequest.newBuilder(URI.create(createCollectionURL)).build(); 56 | HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); 57 | 58 | if (response.statusCode() != 200) { 59 | throw new IllegalStateException("Cannot set up products collection in Solr: " + new String(response.body().getBytes())); 60 | } 61 | } catch (IOException | InterruptedException e) { 62 | throw new IllegalStateException("Cannot set up products collection in Solr"); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/test/resources/warehouse-test.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | 3 | services: 4 | postgresql: 5 | image: postgres:9.6.12 6 | environment: 7 | - POSTGRES_DB=warehouse 8 | - POSTGRES_USER=postgres 9 | - POSTGRES_PASSWORD=postgres 10 | volumes: 11 | - ./warehouse.sql:/docker-entrypoint-initdb.d/warehouse.sql 12 | 13 | solr: 14 | image: solr:8.9.0 15 | command: 16 | - bash 17 | - "-c" 18 | - "solr -f -cloud" -------------------------------------------------------------------------------- /exercises/03-multi-container/start/src/test/resources/warehouse.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS ITEM; 2 | 3 | CREATE TABLE ITEM( 4 | ID SERIAL PRIMARY KEY, 5 | NAME TEXT NOT NULL, 6 | PRICE NUMERIC NOT NULL 7 | ); -------------------------------------------------------------------------------- /exercises/04-generic-container/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 4 2 | 3 | In this exercise, you will learn how to use Testcontainers to use a generic container. First, you'll create a container object of type `org.testcontainers.containers.GenericContainer`. Later, you'll build the container on-the-fly. 4 | 5 | > **_NOTE:_** You can choose to run the build using Maven or Gradle. Pick the tool you are most comfortable with. 6 | 7 | 1. In the existing test class, create a `GenericContainer` that uses the image named `nginx:1.21.3`. The test code should be able to reach the endpoint of the Ngix container. 8 | 2. Run the test and verify its correct behavior. For Maven the command is `./mvnw test`, for Gradle the command is `./gradlew test`. The console output should indicate that the container has been created and destroyed after the test finished. 9 | 3. Instead of using a pre-built Nginx, build the container using the provided Dockerfile. 10 | 4. Run the test and verify its correct behavior. For Maven the command is `./mvnw test`, for Gradle the command is `./gradlew test`. The console output should indicate that the container has been created and destroyed after the test finished. 11 | 5. Instead of using the Dockerfile, build the container with individual instructions defined by Testcontainers method calls. 12 | 6. Run the test and verify its correct behavior. For Maven the command is `./mvnw test`, for Gradle the command is `./gradlew test`. The console output should indicate that the container has been created and destroyed after the test finished. -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/04-generic-container/solution/dockerfile/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group = 'com.bmuschko' 4 | version = '1.0.0' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_11 7 | targetCompatibility = JavaVersion.VERSION_11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | def junitJupiterVersion = '5.8.1' 15 | testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" 16 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" 17 | 18 | testImplementation platform('org.testcontainers:testcontainers-bom:1.17.3') 19 | testImplementation 'org.testcontainers:junit-jupiter' 20 | testImplementation 'org.slf4j:slf4j-simple:1.7.32' 21 | } 22 | 23 | test { 24 | useJUnitPlatform() 25 | testLogging { 26 | showStandardStreams = true 27 | } 28 | } -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/04-generic-container/solution/dockerfile/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | 7 | com.bmuschko 8 | on-the-fly-nginx-generic-container 9 | 1.0.0 10 | 11 | 12 | 11 13 | UTF-8 14 | 5.8.1 15 | 16 | 17 | 18 | 19 | org.junit.jupiter 20 | junit-jupiter-api 21 | ${junit.jupiter.version} 22 | test 23 | 24 | 25 | org.junit.jupiter 26 | junit-jupiter-engine 27 | ${junit.jupiter.version} 28 | test 29 | 30 | 31 | org.testcontainers 32 | junit-jupiter 33 | test 34 | 35 | 36 | org.slf4j 37 | slf4j-simple 38 | 1.7.32 39 | test 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.testcontainers 47 | testcontainers-bom 48 | 1.17.3 49 | pom 50 | import 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-compiler-plugin 61 | 3.8.1 62 | 63 | ${jdk.version} 64 | ${jdk.version} 65 | ${project.build.sourceEncoding} 66 | 67 | 68 | 69 | maven-surefire-plugin 70 | 2.22.2 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'on-the-fly-nginx-generic-container' -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/src/main/java/com/bmuschko/testcontainers/NginxService.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | public interface NginxService { 4 | void ping(); 5 | } 6 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/src/main/java/com/bmuschko/testcontainers/NginxServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import java.io.IOException; 4 | import java.net.URI; 5 | import java.net.http.HttpClient; 6 | import java.net.http.HttpRequest; 7 | import java.net.http.HttpResponse; 8 | 9 | public class NginxServiceImpl implements NginxService { 10 | private final String endpointUrl; 11 | 12 | public NginxServiceImpl(String endpointUrl) { 13 | this.endpointUrl = endpointUrl; 14 | } 15 | 16 | @Override 17 | public void ping() { 18 | try { 19 | HttpClient client = HttpClient.newHttpClient(); 20 | HttpRequest request = HttpRequest.newBuilder(URI.create(endpointUrl)).build(); 21 | HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); 22 | 23 | if (response.statusCode() != 200) { 24 | throw new IllegalStateException("Cannot reach Ngix server: " + new String(response.body().getBytes())); 25 | } 26 | } catch (IOException | InterruptedException e) { 27 | throw new IllegalStateException("Cannot reach Ngix server"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/src/test/java/com/bmuschko/testcontainers/NginxServiceIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.testcontainers.containers.GenericContainer; 6 | import org.testcontainers.images.builder.ImageFromDockerfile; 7 | import org.testcontainers.junit.jupiter.Container; 8 | import org.testcontainers.junit.jupiter.Testcontainers; 9 | 10 | import java.io.File; 11 | 12 | @Testcontainers 13 | public class NginxServiceIntegrationTest { 14 | private NginxService nginxService; 15 | 16 | @Container 17 | private final GenericContainer nginx = new GenericContainer(new ImageFromDockerfile().withDockerfile(new File("src/test/resources/Dockerfile").toPath())).withExposedPorts(80); 18 | 19 | @BeforeEach 20 | public void setUp() { 21 | String host = nginx.getHost(); 22 | Integer port = nginx.getFirstMappedPort(); 23 | String endpointUrl = "http://" + host + ":" + port; 24 | nginxService = new NginxServiceImpl(endpointUrl); 25 | } 26 | 27 | @Test 28 | public void testPing() { 29 | nginxService.ping(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/dockerfile/src/test/resources/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.2 2 | RUN ["apk", "add", "--update", "nginx"] 3 | CMD ["nginx", "-g", "daemon off;"] 4 | EXPOSE 80 -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/instructions/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/04-generic-container/solution/instructions/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/instructions/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/instructions/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group = 'com.bmuschko' 4 | version = '1.0.0' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_11 7 | targetCompatibility = JavaVersion.VERSION_11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | def junitJupiterVersion = '5.8.1' 15 | testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" 16 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" 17 | 18 | testImplementation platform('org.testcontainers:testcontainers-bom:1.17.3') 19 | testImplementation 'org.testcontainers:junit-jupiter' 20 | testImplementation 'org.slf4j:slf4j-simple:1.7.32' 21 | } 22 | 23 | test { 24 | useJUnitPlatform() 25 | testLogging { 26 | showStandardStreams = true 27 | } 28 | } -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/instructions/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/04-generic-container/solution/instructions/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/instructions/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/instructions/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/instructions/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | 7 | com.bmuschko 8 | on-the-fly-nginx-generic-container 9 | 1.0.0 10 | 11 | 12 | 11 13 | UTF-8 14 | 5.8.1 15 | 16 | 17 | 18 | 19 | org.junit.jupiter 20 | junit-jupiter-api 21 | ${junit.jupiter.version} 22 | test 23 | 24 | 25 | org.junit.jupiter 26 | junit-jupiter-engine 27 | ${junit.jupiter.version} 28 | test 29 | 30 | 31 | org.testcontainers 32 | junit-jupiter 33 | test 34 | 35 | 36 | org.slf4j 37 | slf4j-simple 38 | 1.7.32 39 | test 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.testcontainers 47 | testcontainers-bom 48 | 1.17.3 49 | pom 50 | import 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-compiler-plugin 61 | 3.8.1 62 | 63 | ${jdk.version} 64 | ${jdk.version} 65 | ${project.build.sourceEncoding} 66 | 67 | 68 | 69 | maven-surefire-plugin 70 | 2.22.2 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/instructions/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'on-the-fly-nginx-generic-container' -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/instructions/src/main/java/com/bmuschko/testcontainers/NginxService.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | public interface NginxService { 4 | void ping(); 5 | } 6 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/instructions/src/main/java/com/bmuschko/testcontainers/NginxServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import java.io.IOException; 4 | import java.net.URI; 5 | import java.net.http.HttpClient; 6 | import java.net.http.HttpRequest; 7 | import java.net.http.HttpResponse; 8 | 9 | public class NginxServiceImpl implements NginxService { 10 | private final String endpointUrl; 11 | 12 | public NginxServiceImpl(String endpointUrl) { 13 | this.endpointUrl = endpointUrl; 14 | } 15 | 16 | @Override 17 | public void ping() { 18 | try { 19 | HttpClient client = HttpClient.newHttpClient(); 20 | HttpRequest request = HttpRequest.newBuilder(URI.create(endpointUrl)).build(); 21 | HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); 22 | 23 | if (response.statusCode() != 200) { 24 | throw new IllegalStateException("Cannot reach Ngix server: " + new String(response.body().getBytes())); 25 | } 26 | } catch (IOException | InterruptedException e) { 27 | throw new IllegalStateException("Cannot reach Ngix server"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/instructions/src/test/java/com/bmuschko/testcontainers/NginxServiceIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.testcontainers.containers.GenericContainer; 6 | import org.testcontainers.images.builder.ImageFromDockerfile; 7 | import org.testcontainers.junit.jupiter.Container; 8 | import org.testcontainers.junit.jupiter.Testcontainers; 9 | 10 | import java.io.File; 11 | 12 | @Testcontainers 13 | public class NginxServiceIntegrationTest { 14 | private NginxService nginxService; 15 | 16 | @Container 17 | private final GenericContainer nginx = new GenericContainer(new ImageFromDockerfile().withDockerfileFromBuilder(builder -> 18 | builder.from("alpine:3.2") 19 | .run("apk add --update nginx") 20 | .cmd("nginx", "-g", "daemon off;") 21 | .build())).withExposedPorts(80); 22 | 23 | @BeforeEach 24 | public void setUp() { 25 | String host = nginx.getHost(); 26 | Integer port = nginx.getFirstMappedPort(); 27 | String endpointUrl = "http://" + host + ":" + port; 28 | nginxService = new NginxServiceImpl(endpointUrl); 29 | } 30 | 31 | @Test 32 | public void testPing() { 33 | nginxService.ping(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/prebuilt/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/04-generic-container/solution/prebuilt/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/prebuilt/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/prebuilt/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group = 'com.bmuschko' 4 | version = '1.0.0' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_11 7 | targetCompatibility = JavaVersion.VERSION_11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | def junitJupiterVersion = '5.8.1' 15 | testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" 16 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" 17 | 18 | testImplementation platform('org.testcontainers:testcontainers-bom:1.17.3') 19 | testImplementation 'org.testcontainers:junit-jupiter' 20 | testImplementation 'org.slf4j:slf4j-simple:1.7.32' 21 | } 22 | 23 | test { 24 | useJUnitPlatform() 25 | testLogging { 26 | showStandardStreams = true 27 | } 28 | } -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/prebuilt/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/04-generic-container/solution/prebuilt/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/prebuilt/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/prebuilt/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/prebuilt/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | 7 | com.bmuschko 8 | prebuilt-nginx-generic-container 9 | 1.0.0 10 | 11 | 12 | 11 13 | UTF-8 14 | 5.8.1 15 | 16 | 17 | 18 | 19 | org.junit.jupiter 20 | junit-jupiter-api 21 | ${junit.jupiter.version} 22 | test 23 | 24 | 25 | org.junit.jupiter 26 | junit-jupiter-engine 27 | ${junit.jupiter.version} 28 | test 29 | 30 | 31 | org.testcontainers 32 | junit-jupiter 33 | test 34 | 35 | 36 | org.slf4j 37 | slf4j-simple 38 | 1.7.32 39 | test 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.testcontainers 47 | testcontainers-bom 48 | 1.17.3 49 | pom 50 | import 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-compiler-plugin 61 | 3.8.1 62 | 63 | ${jdk.version} 64 | ${jdk.version} 65 | ${project.build.sourceEncoding} 66 | 67 | 68 | 69 | maven-surefire-plugin 70 | 2.22.2 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/prebuilt/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'prebuilt-nginx-generic-container' -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/prebuilt/src/main/java/com/bmuschko/testcontainers/NginxService.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | public interface NginxService { 4 | void ping(); 5 | } 6 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/prebuilt/src/main/java/com/bmuschko/testcontainers/NginxServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import java.io.IOException; 4 | import java.net.URI; 5 | import java.net.http.HttpClient; 6 | import java.net.http.HttpRequest; 7 | import java.net.http.HttpResponse; 8 | 9 | public class NginxServiceImpl implements NginxService { 10 | private final String endpointUrl; 11 | 12 | public NginxServiceImpl(String endpointUrl) { 13 | this.endpointUrl = endpointUrl; 14 | } 15 | 16 | @Override 17 | public void ping() { 18 | try { 19 | HttpClient client = HttpClient.newHttpClient(); 20 | HttpRequest request = HttpRequest.newBuilder(URI.create(endpointUrl)).build(); 21 | HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); 22 | 23 | if (response.statusCode() != 200) { 24 | throw new IllegalStateException("Cannot reach Ngix server: " + new String(response.body().getBytes())); 25 | } 26 | } catch (IOException | InterruptedException e) { 27 | throw new IllegalStateException("Cannot reach Ngix server"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /exercises/04-generic-container/solution/prebuilt/src/test/java/com/bmuschko/testcontainers/NginxServiceIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.testcontainers.containers.GenericContainer; 6 | import org.testcontainers.junit.jupiter.Container; 7 | import org.testcontainers.junit.jupiter.Testcontainers; 8 | import org.testcontainers.utility.DockerImageName; 9 | 10 | @Testcontainers 11 | public class NginxServiceIntegrationTest { 12 | private NginxService nginxService; 13 | 14 | @Container 15 | private final GenericContainer nginx = new GenericContainer(DockerImageName.parse("nginx:1.21.3")).withExposedPorts(80); 16 | 17 | @BeforeEach 18 | public void setUp() { 19 | String host = nginx.getHost(); 20 | Integer port = nginx.getFirstMappedPort(); 21 | String endpointUrl = "http://" + host + ":" + port; 22 | nginxService = new NginxServiceImpl(endpointUrl); 23 | } 24 | 25 | @Test 26 | public void testPing() { 27 | nginxService.ping(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /exercises/04-generic-container/start/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /exercises/04-generic-container/start/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/04-generic-container/start/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exercises/04-generic-container/start/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /exercises/04-generic-container/start/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | group = 'com.bmuschko' 4 | version = '1.0.0' 5 | 6 | sourceCompatibility = JavaVersion.VERSION_11 7 | targetCompatibility = JavaVersion.VERSION_11 8 | 9 | repositories { 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | def junitJupiterVersion = '5.8.1' 15 | testImplementation "org.junit.jupiter:junit-jupiter-api:$junitJupiterVersion" 16 | testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitJupiterVersion" 17 | 18 | testImplementation platform('org.testcontainers:testcontainers-bom:1.17.3') 19 | testImplementation 'org.testcontainers:junit-jupiter' 20 | testImplementation 'org.slf4j:slf4j-simple:1.7.32' 21 | } 22 | 23 | test { 24 | useJUnitPlatform() 25 | testLogging { 26 | showStandardStreams = true 27 | } 28 | } -------------------------------------------------------------------------------- /exercises/04-generic-container/start/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/04-generic-container/start/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /exercises/04-generic-container/start/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /exercises/04-generic-container/start/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /exercises/04-generic-container/start/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | jar 6 | 7 | com.bmuschko 8 | on-the-fly-nginx-generic-container 9 | 1.0.0 10 | 11 | 12 | 11 13 | UTF-8 14 | 5.8.1 15 | 16 | 17 | 18 | 19 | org.junit.jupiter 20 | junit-jupiter-api 21 | ${junit.jupiter.version} 22 | test 23 | 24 | 25 | org.junit.jupiter 26 | junit-jupiter-engine 27 | ${junit.jupiter.version} 28 | test 29 | 30 | 31 | org.testcontainers 32 | junit-jupiter 33 | test 34 | 35 | 36 | org.slf4j 37 | slf4j-simple 38 | 1.7.32 39 | test 40 | 41 | 42 | 43 | 44 | 45 | 46 | org.testcontainers 47 | testcontainers-bom 48 | 1.17.3 49 | pom 50 | import 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | org.apache.maven.plugins 60 | maven-compiler-plugin 61 | 3.8.1 62 | 63 | ${jdk.version} 64 | ${jdk.version} 65 | ${project.build.sourceEncoding} 66 | 67 | 68 | 69 | maven-surefire-plugin 70 | 2.22.2 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /exercises/04-generic-container/start/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'on-the-fly-nginx-generic-container' -------------------------------------------------------------------------------- /exercises/04-generic-container/start/src/main/java/com/bmuschko/testcontainers/NginxService.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | public interface NginxService { 4 | void ping(); 5 | } 6 | -------------------------------------------------------------------------------- /exercises/04-generic-container/start/src/main/java/com/bmuschko/testcontainers/NginxServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import java.io.IOException; 4 | import java.net.URI; 5 | import java.net.http.HttpClient; 6 | import java.net.http.HttpRequest; 7 | import java.net.http.HttpResponse; 8 | 9 | public class NginxServiceImpl implements NginxService { 10 | private final String endpointUrl; 11 | 12 | public NginxServiceImpl(String endpointUrl) { 13 | this.endpointUrl = endpointUrl; 14 | } 15 | 16 | @Override 17 | public void ping() { 18 | try { 19 | HttpClient client = HttpClient.newHttpClient(); 20 | HttpRequest request = HttpRequest.newBuilder(URI.create(endpointUrl)).build(); 21 | HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); 22 | 23 | if (response.statusCode() != 200) { 24 | throw new IllegalStateException("Cannot reach Ngix server: " + new String(response.body().getBytes())); 25 | } 26 | } catch (IOException | InterruptedException e) { 27 | throw new IllegalStateException("Cannot reach Ngix server"); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /exercises/04-generic-container/start/src/test/java/com/bmuschko/testcontainers/NginxServiceIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.bmuschko.testcontainers; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | import org.testcontainers.containers.GenericContainer; 6 | import org.testcontainers.images.builder.ImageFromDockerfile; 7 | import org.testcontainers.junit.jupiter.Container; 8 | import org.testcontainers.junit.jupiter.Testcontainers; 9 | 10 | import java.io.File; 11 | 12 | @Testcontainers 13 | public class NginxServiceIntegrationTest { 14 | private NginxService nginxService; 15 | 16 | @BeforeEach 17 | public void setUp() { 18 | String host = nginx.getHost(); 19 | Integer port = nginx.getFirstMappedPort(); 20 | String endpointUrl = "http://" + host + ":" + port; 21 | nginxService = new NginxServiceImpl(endpointUrl); 22 | } 23 | 24 | @Test 25 | public void testPing() { 26 | nginxService.ping(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /exercises/04-generic-container/start/src/test/resources/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.2 2 | RUN ["apk", "add", "--update", "nginx"] 3 | CMD ["nginx", "-g", "daemon off;"] 4 | EXPOSE 80 -------------------------------------------------------------------------------- /exercises/05-ci-github-actions/instructions.md: -------------------------------------------------------------------------------- 1 | # Exercise 5 2 | 3 | In this exercise, you will execute a Testcontainers test suite on a Continuous Integration product. The CI product of choice will be [GitHub Actions](https://github.com/features/actions). 4 | 5 | > **_NOTE:_** You can choose to run the build using Maven or Gradle. Pick the tool you are most comfortable with. 6 | 7 | 1. Create a new repository on GitHub. You can give it any name you like. 8 | 2. Follow the instructions in the GitHub repository to create a local repository. 9 | 3. Copy the solution of one of the previous exercises into the local repository. 10 | 4. Add the GitHub Actions workflow file at the appropriate location. 11 | 5. The GitHub Actions workflow should exercise the test Gradle or Maven as a step in the job. 12 | 6. Commit and push the code. 13 | 7. Observe the execution of the build in the "Actions" tab of your GitHub repository. -------------------------------------------------------------------------------- /exercises/05-ci-github-actions/solution/images/github-actions-execution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/05-ci-github-actions/solution/images/github-actions-execution.png -------------------------------------------------------------------------------- /exercises/05-ci-github-actions/solution/images/github-repository-creation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/exercises/05-ci-github-actions/solution/images/github-repository-creation.png -------------------------------------------------------------------------------- /exercises/05-ci-github-actions/solution/solution.md: -------------------------------------------------------------------------------- 1 | # Solution 2 | 3 | Create the repository on GitHub. In this case, the repository name is `redis-generic-container`. You can chose to set the repository visibility to "public" or "private". 4 | 5 | ![GitHub repository creation](./images/github-repository-creation.png) 6 | 7 | Create a local directory with the same name the repository. 8 | 9 | ``` 10 | $ mkdir redis-generic-container 11 | $ cd redis-generic-container 12 | ``` 13 | 14 | Copy the contents of one of the solution directories e.g. `01-redis-generic-container/solution` into the directory. Create the GitHub Actions workflow in the file `.github/workflows/build.yaml` with the following contents. 15 | 16 | ``` 17 | name: Build [Linux] 18 | on: [push, pull_request] 19 | 20 | jobs: 21 | build: 22 | name: Build 23 | runs-on: ubuntu-20.04 24 | steps: 25 | - uses: actions/checkout@v2 26 | - name: Setup Java 27 | uses: actions/setup-java@v1 28 | with: 29 | java-version: 11 30 | - name: Build with Gradle 31 | uses: gradle/gradle-build-action@v2 32 | with: 33 | arguments: build 34 | - name: Build with Maven 35 | run: ./mvnw test 36 | ``` 37 | 38 | Initialize the directory as Git repository, commit the files, and push them to the remote repository. Note that the `git remote add origin` needs to point to your repository URL. 39 | 40 | ``` 41 | $ git init 42 | $ git add . 43 | $ git commit . -m "first commit" 44 | $ git remote add origin git@github.com:bmuschko/redis-generic-container.git 45 | $ git push -u origin master 46 | ``` 47 | 48 | You will see the build executions under the "Actions" tab of your Git repository. 49 | 50 | ![GitHub Actions execution](./images/github-actions-execution.png) 51 | -------------------------------------------------------------------------------- /prerequisites/instructions.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | Excercise will require the tooling listed below. Ensure that all of those tools have been installed before attending the training if you want to following along. The training does not reserve time for setting up or verifying the installed tools or their respective versions. 4 | 5 | ## Tools and Accounts 6 | 7 | * Java 11 OpenJDK and Docker Engine 8 | * A GitHub account 9 | * A Docker Hub account 10 | 11 | ## Recommended Skills 12 | 13 | * A basic understanding of Docker and the Java programming language 14 | * Hands-on experience with common Java frameworks and dependencies 15 | * Experience with writing test code with the test framework JUnit 4 or JUnit 5 -------------------------------------------------------------------------------- /slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bmuschko/testcontainers-integration-testing/71517bc44b9f659260a600590342aa33bb6b8b0d/slides.pdf --------------------------------------------------------------------------------