删除集成测试中创建 bean 的异常,它不会出现在标准应用程序启动时

Remove exception with bean creation on integration test, which does not appear on standard application start

我有 spring 运行良好的启动应用程序,但是当我开始进行集成测试时,我发现项目中存在循环依赖:

@Service
public class CrowdManagerSyncService {
    private final CrowdManagerSyncScheduler crowdManagerSyncScheduler;

    @Autowired
    public CrowdManagerSyncService(CrowdManagerSyncScheduler crowdManagerSyncScheduler) {
        this.crowdManagerSyncScheduler = Objects.requireNonNull(crowdManagerSyncScheduler);
    }
}

@Component
public class CrowdManagerSyncScheduler {
    @Autowired
    private CrowdManagerSyncService crowdManagerSyncService;
}

这不是我的代码,我现在还没准备好重写它。但它在生产中运行得非常好。在我的集成测试中

@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
@WithMockUser(roles={"ADMIN"})
@ContextConfiguration(classes = {AdminConsoleApplication.class, DataSourceAutoConfiguration.class,
        MockMvcAutoConfiguration.class, MockMvcWebDriverAutoConfiguration.class})
public class UserControllerTest {
    @Autowired
    private MockMvcHtmlUnitDriverBuilder builder;
    private WebDriver webDriver;

    @Before
    public void setUp() throws Exception {
        webDriver = builder.build();
    }
}

我发现异常:

Error creating bean with name 'crowdManagerSyncService': Requested bean is currently in creation: Is there an unresolvable circular reference?

所以,我的问题是:如何在不消除可怕的循环依赖的情况下在测试中忽略这个问题?它在生产中运行良好,所以很确定有一些方法可以在不更改代码的情况下启动测试上下文。

@WebMvcTest 不适合 "proper" 集成测试。

来自 the api docs

Can be used when a test focuses only on Spring MVC components.

但是,您随后使用 @ContextConfiguration 实质上将整个应用程序添加到测试中。

删除 @ContextConfiguration 并改为将 @MockBean CrowdManagerSyncService 自动连接到您的测试中。 这将创建 CrowdManagerSyncService 的模拟版本并将其注入测试应用程序上下文中的 UserController

@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
@WithMockUser(roles={"ADMIN"})
public class UserControllerTest {
    @Autowired
    private MockMvcHtmlUnitDriverBuilder builder;

    @MockBean
    private CrowdManagerSyncService service;

    private WebDriver webDriver;

    @Before
    public void setUp() throws Exception {
        webDriver = builder.build();
    }

    @Test
    public void shouldWork() {
        when(service.doStuff())
            .thenReturn("Hello"); // regular Mockito mocking
    }
}

如果您只是想测试 UserController 并回避循环依赖问题,这是合适的,因为在任何地方都没有 "real" CrowdManagerSyncService 的实例化。

您还可以将 @WebMvcTest@ContextConfiguration 替换为 @SpringBootTest(它像生产一样引导应用程序)和 @AutoConfigureMockMvc(它将真正的 HTTP 内容替换为MockMvc).