不确定为什么这个单元测试有效。 Spring ConfigurationContext 更新或创建新对象?

Not sure why this unit test works. Spring ConfigurationContext updating or creating new object?

我通过了单元测试,但我不知道它为什么会起作用。在 IntelliJ 中设置调试点表明 Autowired 属性 始终为空。我已经在 GitHub 上发布了该项目,以防有人想看一看:https://github.com/leonj1/spring_spark

下面是我在其中注释了调试点(例如#1、#2、#3)的代码。 当调试点 #1 + #2 被命中时,@Autowired PersonRoute personRoute 始终为空。这可能是因为内部静态 class 在 @ContextConfiguration 解析 @Configuration.

之前被实例化了

但是 @ClassRule 中的 testServer 如何获得再水化的 SimpleController ?我知道 Spring 使用 Reflections 来检查和设置属性,但如果它也对已经实例化的对象执行此操作对我来说将是新闻。

我正在努力理解这一点,因为目前不知道让我对测试的信心降低。

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@TestExecutionListeners({
        DependencyInjectionTestExecutionListener.class,
        DbUnitTestExecutionListener.class
})
@DatabaseSetup("persons-entities.xml")
@Transactional
public class SimpleControllerTest {

    @Configuration
    @ComponentScan(basePackages = {"com.jose.sandbox"})
    static class SomeConfig {

        // because @PropertySource doesn't work in annotation only land
        @Bean
        public PropertyPlaceholderConfigurer propConfig() {
            // #3 debug point 3, then shows Spring creating beans
            // but how does SimpleController.class get the property set?
            // does this mean 2 different SimpleController objects exist in the JVM, but only one is "wired"?
            PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
            ppc.setLocation(new ClassPathResource("application.properties"));
            return ppc;
        }
    }

    @Component
    public static class TestControllerTestApplication implements SparkApplication {

        // #1 debug point 1 in IDE shows this as NULL
        @Autowired PersonRoute personRoute;

        @Override
        public void init() {
            new SimpleController(this.personRoute);
        }
    }

    @ClassRule
    public static SparkServer<SimpleControllerTest.TestControllerTestApplication> testServer
            = new SparkServer<>(SimpleControllerTest.TestControllerTestApplication.class, 4567);

    @Test
    public void verifyGetAllPeople() throws Exception {
        // given
        String payload = null;

        // when
        SparkClient.UrlResponse response = testServer.getClient().doMethod("GET", "/people/count", payload);

        // then
        int expected = 3; // because that's how many exist in persons-entities.xml
        assertEquals(200, response.status);
        assertEquals(expected, Integer.parseInt(response.body));
        assertNotNull(testServer.getApplication());
    }

    @Mock
    Response response;
}

这是控制器,初始调试点为 Autowired 属性 显示 NULL。调试点再也不会被击中,那么这是如何设置的?

@Component
public class SimpleController {

    // #2 debug point 2, also shows this as NULL
    @Autowired PersonRoute personRoute;

    public SimpleController() {}

    public SimpleController(PersonRoute personRoute) {
        this.personRoute = personRoute;
    }

    @PostConstruct
    public void init() {
        get("/people/count", this.personRoute);
    }
}

任何帮助将不胜感激,因为在理解之前很难在此测试的基础上进行构建。

But then how does testServer in @ClassRule get the rehydrated SimpleController?

没有。

其实你可以在你的测试中将下面的代码全部注释掉,测试依然通过

// @Component
public static class TestControllerTestApplication implements SparkApplication {

    // #1 debug point 1 in IDE shows this as NULL
    // @Autowired PersonRoute personRoute;

    @Override
    public void init() {
        // new SimpleController(this.personRoute);
    }
}

Here is the Controller which initial debug point shows NULL for the Autowired property. The debug point never gets hit again, so how is this being set?

Spring 使用反射将 @Autowired PersonRoute 注入 SimpleController

基本上,发生的事情如下。

  1. 您指示 Spring 从 com.jose.sandbox 基础包开始执行组件扫描,这导致完全初始化 PersonRepositoryPersonRouteSimpleController beans .
  2. PersonRoute bean 被注入到 SimpleController bean 中。
  3. SimpleController bean 的 init() 方法被 Spring 调用,因为你用 @PostConstruct 注释了它,并且通过静态 [= 向 Spark 注册了路由 get(...)方法调用。
  4. 然后,Spark 测试框架在 JUnit 4 规则的 "before class" 阶段实例化您的 TestControllerTestApplication,但该路由已经注册。因此,您的 TestControllerTestApplication 不需要实际做任何事情。这就是为什么我注释掉上面的代码。

换句话说,您只需要空的 TestControllerTestApplication 实现,因为 Spark 测试框架中的 SparkServer 需要它。没有 Spring 实现它是有意义的,但是由于您使用 Spring 配置东西的方式,没有必要 实现 它。

希望这能为您澄清一些事情。

此致,

Sam(Spring TestContext Framework 的作者)