如何基于可执行Docker镜像创建Cucumber测试?附Java示例代码
实现基于Docker镜像的Cucumber测试方案
Got it, let's walk through a complete implementation plan for setting up Cucumber tests against your Dockerized GenerateNumber app. I'll break this down into clear, actionable steps so you can get up and running quickly.
1. 测试项目结构
First, let's lay out the standard Cucumber project structure to keep things organized:
src/test/resources/features/generate_number.feature- Gherkin测试场景(人类可读的测试用例)src/test/java/com/yourcompany/steps/GenerateNumberSteps.java- 步骤定义(将Gherkin映射为Java代码)src/test/java/com/yourcompany/RunCucumberTests.java- JUnit运行器,用于执行Cucumber测试pom.xml(或build.gradle) - 依赖配置,包含Cucumber、Docker客户端和测试库
2. 编写Gherkin测试场景
创建generate_number.feature文件,包含以下测试场景,覆盖应用的核心功能:
Feature: Generate Odd/Even Numbers As a user of the GenerateNumber app I want to verify that the Dockerized app generates correct numbers based on input parameters Scenario: Generate odd numbers when passing ODD parameter When I run the Dockerized app with parameter "ODD" Then the output should contain all odd numbers from 0 to 1000 And no even numbers should be present in the output Scenario: Generate even numbers when passing EVEN parameter When I run the Dockerized app with parameter "EVEN" Then the output should contain all even numbers from 0 to 1000 And no odd numbers should be present in the output Scenario: No output when no parameter is provided When I run the Dockerized app without any parameters Then the output should be empty
3. 实现步骤定义(结合Docker执行)
步骤定义类将使用docker-java库启动Docker容器、运行应用、捕获输出并验证结果。记得把generate-number:latest替换为你实际的Docker镜像名称:
package com.yourcompany.steps; import io.cucumber.java.en.Then; import io.cucumber.java.en.When; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.CreateContainerResponse; import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; import com.github.dockerjava.transport.DockerHttpClient; import org.junit.Assert; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; public class GenerateNumberSteps { private ByteArrayOutputStream outputStream; private final DockerClient dockerClient; private static final String DOCKER_IMAGE = "generate-number:latest"; // 更新为你的镜像名称 public GenerateNumberSteps() { // 初始化Docker客户端 DefaultDockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder().build(); DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder() .dockerHost(config.getDockerHost()) .sslConfig(config.getSSLConfig()) .maxConnections(100) .connectionTimeout(Duration.ofSeconds(30)) .responseTimeout(Duration.ofSeconds(45)) .build(); this.dockerClient = DockerClientImpl.getInstance(config, httpClient); } @When("I run the Dockerized app with parameter {string}") public void runAppWithParameter(String param) throws IOException { outputStream = new ByteArrayOutputStream(); // 创建容器并指定应用命令 CreateContainerResponse container = dockerClient.createContainerCmd(DOCKER_IMAGE) .withCmd("java", "-jar", "GenerateNumber.jar", param) .withAttachStdout(true) .withAttachStderr(true) .exec(); try { // 启动容器并捕获输出 dockerClient.startContainerCmd(container.getId()).exec(); dockerClient.logContainerCmd(container.getId()) .withStdOut(true) .withStdErr(true) .withFollowStream(true) .withTailAll() .exec(new com.github.dockerjava.api.async.ResultCallback.Adapter<Frame>() { @Override public void onNext(Frame frame) { outputStream.write(frame.getPayload()); } }).awaitCompletion(); } finally { // 测试结束后清理:停止并删除容器 dockerClient.stopContainerCmd(container.getId()).exec(); dockerClient.removeContainerCmd(container.getId()).exec(); } } @When("I run the Dockerized app without any parameters") public void runAppWithoutParameters() throws IOException { outputStream = new ByteArrayOutputStream(); CreateContainerResponse container = dockerClient.createContainerCmd(DOCKER_IMAGE) .withCmd("java", "-jar", "GenerateNumber.jar") .withAttachStdout(true) .withAttachStderr(true) .exec(); try { dockerClient.startContainerCmd(container.getId()).exec(); dockerClient.logContainerCmd(container.getId()) .withStdOut(true) .withStdErr(true) .withFollowStream(true) .withTailAll() .exec(new com.github.dockerjava.api.async.ResultCallback.Adapter<Frame>() { @Override public void onNext(Frame frame) { outputStream.write(frame.getPayload()); } }).awaitCompletion(); } finally { dockerClient.stopContainerCmd(container.getId()).exec(); dockerClient.removeContainerCmd(container.getId()).exec(); } } @Then("the output should contain all odd numbers from 0 to 1000") public void verifyAllOddNumbersPresent() { List<Integer> outputNumbers = getOutputNumbers(); List<Integer> expectedOdds = IntStream.rangeClosed(0, 1000) .filter(n -> n % 2 != 0) .boxed() .collect(Collectors.toList()); Assert.assertEquals("缺少预期的奇数", expectedOdds.size(), outputNumbers.size()); Assert.assertTrue("输出未包含所有奇数", outputNumbers.containsAll(expectedOdds)); } @Then("no even numbers should be present in the output") public void verifyNoEvenNumbers() { List<Integer> outputNumbers = getOutputNumbers(); boolean hasEven = outputNumbers.stream().anyMatch(n -> n % 2 == 0); Assert.assertFalse("输出包含意外的偶数", hasEven); } @Then("the output should contain all even numbers from 0 to 1000") public void verifyAllEvenNumbersPresent() { List<Integer> outputNumbers = getOutputNumbers(); List<Integer> expectedEvens = IntStream.rangeClosed(0, 1000) .filter(n -> n % 2 == 0) .boxed() .collect(Collectors.toList()); Assert.assertEquals("缺少预期的偶数", expectedEvens.size(), outputNumbers.size()); Assert.assertTrue("输出未包含所有偶数", outputNumbers.containsAll(expectedEvens)); } @Then("no odd numbers should be present in the output") public void verifyNoOddNumbers() { List<Integer> outputNumbers = getOutputNumbers(); boolean hasOdd = outputNumbers.stream().anyMatch(n -> n % 2 != 0); Assert.assertFalse("输出包含意外的奇数", hasOdd); } @Then("the output should be empty") public void verifyEmptyOutput() { String output = outputStream.toString().trim(); Assert.assertTrue("预期空输出但得到内容", output.isEmpty()); } private List<Integer> getOutputNumbers() { String output = outputStream.toString().trim(); if (output.isEmpty()) { return new ArrayList<>(); } // 根据应用的输出格式调整分割规则(比如换行、空格) String[] numberStrings = output.split("\\n|\\s+"); List<Integer> numbers = new ArrayList<>(); for (String s : numberStrings) { try { numbers.add(Integer.parseInt(s.trim())); } catch (NumberFormatException e) { // 忽略非数字输出(比如日志或错误信息) } } return numbers; } }
4. JUnit运行器类
创建运行器类来执行你的Cucumber测试:
package com.yourcompany; import io.cucumber.junit.Cucumber; import io.cucumber.junit.CucumberOptions; import org.junit.runner.RunWith; @RunWith(Cucumber.class) @CucumberOptions( features = "src/test/resources/features", glue = "com.yourcompany.steps", plugin = {"pretty", "html:target/cucumber-reports.html"} // 生成可读的HTML测试报告 ) public class RunCucumberTests { }
5. 依赖配置(Maven)
将以下依赖添加到pom.xml中,引入Cucumber、Docker客户端和测试工具:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.yourcompany</groupId> <artifactId>generate-number-cucumber-tests</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <!-- Cucumber --> <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-java</artifactId> <version>7.14.0</version> <scope>test</scope> </dependency> <dependency> <groupId>io.cucumber</groupId> <artifactId>cucumber-junit</artifactId> <version>7.14.0</version> <scope>test</scope> </dependency> <!-- Docker Java Client --> <dependency> <groupId>com.github.docker-java</groupId> <artifactId>docker-java</artifactId> <version>3.3.3</version> <scope>test</scope> </dependency> <dependency> <groupId>com.github.docker-java</groupId> <artifactId>docker-java-transport-httpclient5</artifactId> <version>3.3.3</version> <scope>test</scope> </dependency> <!-- JUnit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13.2</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-surefire-plugin</artifactId> <version>3.1.2</version> <configuration> <includes> <include>**/RunCucumberTests.java</include> </includes> </configuration> </plugin> </plugins> </build> </project>
6. 运行测试
运行前请确保:
- 测试机器上的Docker守护进程正在运行
- 你的
generate-number:latestDocker镜像已构建并存在于本地仓库 - 当前用户有权限访问Docker(无需sudo)
运行测试的方式:
- Maven命令:
mvn test - 或者在IDE中直接执行
RunCucumberTests类
关键注意事项
- 更新步骤定义类中的
DOCKER_IMAGE常量,匹配你实际的Docker镜像名称 - 根据应用的数字输出格式,调整
getOutputNumbers()方法中的分割规则(比如如果每个数字单独一行,使用\\n) - 如果应用的JAR文件名不是
GenerateNumber.jar,请修改步骤定义中withCmd的参数
内容的提问来源于stack exchange,提问作者Mark Estrada
相关产品推荐
相关产品推荐

