Using Podman to run Testcontainers in a Quarkus project
Yassine Benabbas
Posted on November 29, 2022
In the area of container engines, podman seems to be a relevant alternative to docker, at least during the development phase on a Windows or macOS machine. Indeed, both podman and podman-desktop, it's desktop UI, do not require a paid licence. Which is not the case anymore for Docker-Desktop under certain conditions (it's still free for individual developers, education, open source communities, and small businesses). In addition to that, podman is daemonless and can run in rootless mode.
This article shows how to use podman as a container engine instead of Docker and Docker-Desktop on Windows and macOS. We'll see how to install this tool and how to configure it to run tests that rely on Testcontainers. So let's get started with the prerequisites.
Prerequisites
Even though you can use the official installers, I suggest to use package managers moving forward.
- On Windows, you can use scoop
On macOS, you can use sdkman for java-related tools ad homebrew for the others.
Java JDK 17
podman
Optionnal: podman-desktop
On Windows, WSL needs to be enabled
Start and configure podman
Open a terminal and run these commands:
-
podman machine init
, which initializes a new VM. You just need to run it once. -
podman machine start
, which starts the VM. You need to re-run it after each reboot. - Podman-desktop allows to configure the podman machine to auto-start. You can start if from the apps menu.
- Now we are ready to launch containers. Run
podman run hello
which launches a basic container. - The last thing we need to configure is to set this environment variable to
TESTCONTAINERS_RYUK_DISABLED
totrue
The next section demonstrates that we can use Testcontainers through a sample project.
Sample Quarkus project that uses Testcontainers
Let's create with an app with Quarkus which consists of a REST API that backs its data on a postgreSQL database.
Quarkus provides zero-confguration databases for development and test modes (called DEV SERVICES FOR DATABASES). It relies on a containerization tool, such as docker or podman, for some databases. Since we have podman installed and configured, we are ready to go.
In order to create a starter project, open code.quarkus.io and choose JDBC Driver - PostgreSQL extension and any other extensions that you'd like to use for you project.
In the following, I have selected the following extensions just for fun š.
- YAML Configuration
- JDBC Driver - PostgreSQL
- Hibernate ORM with Panache
- Hibernate ORM with Panache and Kotlin
- Kotlin
- RESTEasy Reactive
- RESTEasy Reactive Kotlin Serialization
I also selected Gradle with Kotlin SDL build tool, but you can use any available one.
Once the applications information and extensions are selected, generate and download the zip archive. After that, unzip that archive and open a terminal on the root folder of the project (the one contains pom.xml, build.gradle or build.gradle.kts).
Next, run the tests using .\gradlew.bat test
(if you are using powershell). You should see the following output:
********************************************************************************
Ryuk has been disabled. This can cause unexpected behavior in your environment.
********************************************************************************
2022-11-27 00:16:55,210 INFO [org.tes.DockerClientFactory] (build-57) Checking the system...
2022-11-27 00:16:55,212 INFO [org.tes.DockerClientFactory] (build-57) ?? Docker server version should be at least 1.6.0
2022-11-27 00:16:55,241 INFO [org.tes.uti.ImageNameSubstitutor] (build-57) Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor')
2022-11-27 00:16:55,284 INFO [doc.io/postgres:14]] (build-57) Pulling docker image: docker.io/postgres:14. Please be patient; this may take some time but only needs to be done once.
...
BUILD SUCCESSFUL in 21s
9 actionable tasks: 1 executed, 8 up-to-date
The tests were executed successfully. The output proves that a container image has been downloaded and started. We can deduce that Podman was successfully able to interact with Testcontaiers, which is the library that is responsible for testing with containers.
Let's analyse this output. The following line means that the environment variable that we set earlier was detected.
Ryuk has been disabled. This can cause unexpected behavior in your environment.
This line means that postgreSQL container image docker.io/postgres:14 is being downloaded.
2022-11-27 00:16:55,284 INFO [doc.io/postgres:14]] (build-57) Pulling docker image: docker.io/postgres:14. Please be patient; this may take some time but only needs to be done once.
Finally, we can check with podman the presence of a postgreSQL image. Open a terminal and run podman images
, you should see an output similar to this one.
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/library/postgres 14 c920f82d2df2 11 days ago 384 MB
Indeed, the postgres image is present. We have used containers with Podman. Horaaay !
Testing with an entity
Even though we validated that podman works with testcontainers, we did not really use the database yet. Thus, for the sake of completion, let's create a Panache entity as well as a repository, and update our REST resource and tests to use this entity.
Here is the code on the app side:
@Entity
@Serializable
class Greeting : PanacheEntity() {
lateinit var message: String
}
@ApplicationScoped
class GreetingRepository : PanacheRepository<Greeting> {
fun findByPrefix(prefix: String) = list("message like ?1", "$prefix%")
}
@Path("/")
class GreetingResource {
@Inject
lateinit var greetingRepository: GreetingRepository
@GET
@Path("/greetings/{prefix}")
@Produces(MediaType.APPLICATION_JSON)
fun greetings(@PathParam("prefix") prefix: String) = greetingRepository.findByPrefix(prefix).orEmpty()
}
The library used for entity mangement is called Panache and allows to use the repository pattern with Hibernate.
ā We added @Serializable
on the entity so that it can be parsed into and from JSON. However, the Quarkus project generator did not add an important plugin that makes serialization work with Kotlin. Please add this plugin to build.gradle.kts in order to enable this feature: id("org.jetbrains.kotlin.plugin.serialization") version "1.7.21"
The plugin part should look like this:
plugins {
kotlin("jvm") version "1.7.21"
kotlin("plugin.allopen") version "1.7.21"
// add this line
id("org.jetbrains.kotlin.plugin.serialization") version "1.7.21"
id("io.quarkus")
}
This is the test code. The method prepareData
adds initial data to the database and testHelloEndpoint
tests the above endpoint. The library that is used for testing the REST API is called rest-assured and provides a nice DSL (Domain Specific Language) with functions such as given
and when
.
@QuarkusTest
class GreetingResourceTest {
@Inject
lateinit var greetingRepository: GreetingRepository
@Transactional
@BeforeEach
fun prepareData() {
greetingRepository.deleteAll()
val greeting = Greeting()
greeting.message = "hello world"
greetingRepository.persist(greeting)
greetingRepository.flush()
}
@Test
fun testHelloEndpoint() {
given()
.`when`().get("/greetings/hello")
.then()
.statusCode(HttpStatus.SC_OK)
.body("$.size()", equalTo(1))
.body("message", hasItem("hello world"))
}
}
As stated earlier, no database configuration is needed, thanks to Quarkus' Dev Services. We just need to run .\gradlew.bat test
to run the tests and ./gradlew quarkusDev
to run the development server.
Conclusion
This article showed how to use podman as a container tool on a Quarkus + testcontainers project. The most important setting that make Podman and Testcontainers interact with each other is setting the environment variable TESTCONTAINERS_RYUK_DISABLED
to true
.
Happy coding
Posted on November 29, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.