2

我有一个包含 3 个集成测试类的项目:A、B 和 C。我对代码进行了更改,作为这些更改的一部分,我添加了一个 @MockBean 来测试类 A。

这是一个由每个集成测试类扩展的类:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = MyApplication.class, webEnvironment = RANDOM_PORT)
@TestPropertySource(locations = "classpath:application-test.yml")
@ActiveProfiles(profiles = {"default", "test"})
public abstract class IntegrationTest {

    @Value("${local.server.port}")
    private int serverPort;

    @Autowired
    private ObjectMapper objectMapper;

    @Before
    public void setUpIntegrationTest() {
        RestAssured.port = serverPort;
        RestAssured.config = RestAssuredConfig.config()
                .logConfig(LogConfig.logConfig()
                        .enableLoggingOfRequestAndResponseIfValidationFails()
                        .enablePrettyPrinting(true))
                .objectMapperConfig(objectMapperConfig()
                        .jackson2ObjectMapperFactory((cls, charset) -> objectMapper)
                        .defaultObjectMapperType(ObjectMapperType.JACKSON_2))
                .jsonConfig(jsonConfig().numberReturnType(BIG_DECIMAL))
                .redirect(new RedirectConfig().followRedirects(false));
    }
}

现在是一个具体的测试类:

import org.springframework.boot.test.mock.mockito.MockBean;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doNothing;

public class TestClassA extends IntegrationTest {
    @MockBean
    private SomeBean foo;

    @Before
    @Override
    public void setUpIntegrationTest() {
        super.setUpIntegrationTest();
        doNothing().when(foo).fooMethod(any(SomeClass.class), any(SomeOtherClass.class));
    }

    @Test
    public void testCaseX() {
        given()
            .body("{\"foo\": \"bar\"}")
        .when()
            .post("/some/path/")
        .then()
            .statusCode(OK.value());
    }
}

我尝试以三种不同的方式运行测试:

  1. 仅使用模拟 bean 运行测试类 A。所有测试通过。
  2. 构建运行所有测试类的项目。测试类 B 和 C 通过,但 A 在尝试启动码头实例时在应用程序上下文加载期间失败,并且由于地址已在使用中而失败。

引起:org.springframework.beans.BeanInstantiationException:无法实例化[io.github.azagniotov.stubby4j.server.StubbyManager]:工厂方法'stubby'抛出异常;嵌套异常是 java.net.BindException:地址已在使用中 原因:java.net.BindException:地址已在使用中

  1. 移除模拟的 bean,并构建项目。测试类 B 和 C 通过。测试类 A 成功加载应用程序上下文,但一些测试失败(由于模拟给出的缺失行为)。

Jetty 是作为Stubby4j的一部分设置的,它通过以下方式实例化为配置 bean:

@Configuration
public class StubbyConfig {

    @Bean
    public StubbyManager stubby(final ResourceLoader resourceLoader) throws Exception {

        Resource stubbyFile = resourceLoader.getResource("classpath:stubs/stubby.yml");

        if (stubbyFile.exists()) {
            Map<String, String> stubbyConfig = Maps.newHashMap();
            stubbyConfig.put("disable_admin_portal", null);
            stubbyConfig.put("disable_ssl", null);

            File configFile = stubbyFile.getFile();
            Future<List<StubHttpLifecycle>> stubLoadComputation =
                    ConcurrentUtils.constantFuture(new YAMLParser().parse(configFile.getParent(), configFile));

            StubbyManager stubbyManager = new StubbyManagerFactory()
                    .construct(configFile, stubbyConfig, stubLoadComputation);
            stubbyManager.startJetty();

            return stubbyManager;
        } else {
            throw new FileNotFoundException("Could not load stubby.yml");
        }
    }
}

我以两种不同的方式进行了一些调试,在该行中放置了一个断点stubbyManager.startJetty();

  1. 只运行测试类 A。执行只在断点处停止一次。
  2. 使用其他测试类(例如 B)运行测试类 A。B 的执行只停止了一次,但 A 的执行停止了两次。第二次它因上述错误而失败。
  3. 同样,如果我删除模拟 bean 并运行多个测试类,则每个测试类的执行只会在该行停止一次。

显然,我的问题是:为什么 MockedBean 注释会导致这种行为,我该如何避免呢?

提前致谢。

当前项目设置:

  • Spring Boot 版本 1.4.2.RELEASE
  • Stubby4j 版本 4.0.4
4

2 回答 2

0

您可以使用 Spring 的SocketUtils类来查找可用的 TCP 端口,然后您可以将其传递到您的 stubbyConfig 映射中:

...
static final int PORT_RANGE_MIN = 2048;
static final int PORT_RANGE_MAX = 65535;
...
...
public StubbyConfig() {
  this.stubPort = SocketUtils.findAvailableTcpPort(PORT_RANGE_MIN, PORT_RANGE_MAX);
}
...
@Bean
public StubbyManager stubby(final ResourceLoader resourceLoader) throws Exception {
    ...
    if (stubbyFile.exists()) {
       ...
       stubbyConfig.put("stubs", String.valueOf(stubsPort));
       ...
    }
}
...
public int getStubsPort() {
  return this.stubPort;
}

然后,因为您的 StubbyConfig 类上有端口获取器,所以您可以RestAssured.port在运行集成测试时将其传递给

于 2021-12-13T11:10:13.417 回答
0

我只是在尚未完成时启动 Jetty:

if(!stubbyManager.statuses().contains(""Stubs portal configured at")
    stubbyManager.startJetty();
else
    stubbyManager.joinJetty();

如果您找到更好的解决方案,请告诉我

于 2017-07-31T14:15:45.773 回答