I have a project with 3 integration tests classes: A, B and C. I made a change in the code, and as part of those changes I added a @MockBean to test class A.
Here is a class that is extended by every Integration Test class:
@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));
}
}
Now for a concrete test class:
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());
}
}
I have tried to run tests in three different ways:
- Run only test class A, with the mocked bean. All tests pass.
- Build the project which runs all test classes. Test classes B and C pass, but A fails during application context loading while trying to start a jetty instance and fails because the address is already in use.
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [io.github.azagniotov.stubby4j.server.StubbyManager]: Factory method 'stubby' threw exception; nested exception is java.net.BindException: Address already in use Caused by: java.net.BindException: Address already in use
- Remove the mocked bean, and build the project. Test classes B and C pass. Test class A successfully loads the application context, but some tests fail (due to the missing behaviour given by the mock).
Jetty is setup as part of Stubby4j and it is instantiated as a configuration bean in the following way:
@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");
}
}
}
I did some debugging in two different ways, putting a break point in the line stubbyManager.startJetty();
:
- Running just test class A. Execution stopped in the break point only once.
- Running test class A with some other test class (for example, B). Execution stopped only once for B, but twice for A. The second time it failed with the aforementioned error.
- Again, if I remove the mocked bean and run multiple test classes the execution only stops at that line once per test class.
My question, obviously, is: why does the MockedBean annotation cause this behaviour, and how can I avoid it?
Thanks in advance.
Current project setup:
- Spring Boot version 1.4.2.RELEASE
- Stubby4j version 4.0.4
I just start Jetty when not already done with:
Let me know if you find a better solution