@MicronautTest - 限制实例化的内容

@MicronautTest - limit what is instantiated

每当我在我的项目中使用@MicronautTest时,整个应用程序作为一个整体启动,因为某些事情需要在启动时由服务完成,所以它们也会被执行。 当我测试一个简单的控制器时,我不需要整个 Singleton shebang。

我的问题是在测试 Micronaut 时如何限制加载哪些控制器和单例?

我有一个演示问题的小示例

Pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>micronaut-test</artifactId>
    <version>0.1</version>
    <packaging>${packaging}</packaging>

    <parent>
        <groupId>io.micronaut</groupId>
        <artifactId>micronaut-parent</artifactId>
        <version>2.5.1</version>
    </parent>

    <properties>
        <packaging>jar</packaging>
        <jdk.version>11</jdk.version>
        <release.version>11</release.version>
        <micronaut.version>2.5.1</micronaut.version>
        <exec.mainClass>com.example.Application</exec.mainClass>
        <micronaut.runtime>netty</micronaut.runtime>
    </properties>

    <repositories>
        <repository>
            <id>central</id>
            <url>https://repo.maven.apache.org/maven2</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-inject</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-validation</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut.test</groupId>
            <artifactId>micronaut-test-junit5</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-http-client</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-http-server-netty</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-runtime</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>io.micronaut.build</groupId>
                <artifactId>micronaut-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <!-- Uncomment to enable incremental compilation -->
                    <!-- <useIncrementalCompilation>false</useIncrementalCompilation> -->

                    <annotationProcessorPaths combine.children="append">
                    </annotationProcessorPaths>
                    <compilerArgs>
                        <arg>-Amicronaut.processing.group=com.example</arg>
                        <arg>-Amicronaut.processing.module=micronaut-test</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

application.java

package com.example;

import io.micronaut.runtime.Micronaut;

public class Application {

    public static void main(String[] args) {
        Micronaut.run(Application.class, args);
    }
}

一个简单的服务,只要实例化它就会抛出以指示问题

@Singleton
public class SingletonService {

    @EventListener
    public void init(ApplicationStartupEvent event) {
        throw new RuntimeException("I was initialized");
    }
}

一个简单的控制器

@Controller
public class TestController {

    @Get
    public Simple findAll() {
        return new Simple("ATest");
    }

    public static class Simple {
        private final String test;

        public Simple(String test) {
            this.test = test;
        }

        public String getTest() {
            return test;
        }
    }
}

还有一个简单的测试

@MicronautTest
class TestControllerTest {
    @Inject
    @Client("/")
    RxHttpClient rxHttpClient;

    @Test
    void controller() {
        HttpResponse<ByteBuffer> byteBufferHttpResponse = rxHttpClient.exchange("/").blockingFirst();
        assert byteBufferHttpResponse.getStatus().getCode() == 200;
    }
}

测试结果为

[INFO] Running com.example.controller.TestControllerTest
11:01:52.804 [main] INFO  i.m.context.env.DefaultEnvironment - Established active environments: [test]
[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 7.232 s <<< FAILURE! - in com.example.controller.TestControllerTest
[ERROR] com.example.controller.TestControllerTest  Time elapsed: 7.229 s  <<< ERROR!
java.lang.RuntimeException: I was initialized

如何防止测试启动该 SingletonService? Stubbing 是一种选择,但请记住,这是一个简单的演示,说明了更大项目的问题。没有其他直接的方法吗? 根据@MicronautTest 的 docs ,它不应该扫描类路径但它显然是?

以下是我已经尝试但未成功的其他一些配置:

添加禁用 eagerInit 的 ApplicationBuilder

@MicronautTest(application = TestControllerTest.TestApplication.class)
class TestControllerTest {
    public static class TestApplication {
        public static void main(String[] args) {
            Micronaut.build(args)
                    .eagerInitAnnotated(null)
                    .eagerInitSingletons(false)
                    .mainClass(TestApplication.class);
        }
    }
    @Inject
    @Client("/")
    RxHttpClient rxHttpClient;

    @Test
    void controller() {
        HttpResponse<ByteBuffer> byteBufferHttpResponse = rxHttpClient.exchange("/").blockingFirst();
        assert byteBufferHttpResponse.getStatus().getCode() == 200;
    }
}

传递上下文构建器

@MicronautTest(contextBuilder = TestControllerTest.TestContextBuilder.class)
class TestControllerTest {

    public static class TestContextBuilder extends DefaultApplicationContextBuilder {
        public TestContextBuilder() {
            eagerInitSingletons(false);
            eagerInitAnnotated(null);
            eagerInitConfiguration(false);
        }
    }

    @Inject
    @Client("/")
    RxHttpClient rxHttpClient;

    @Test
    void controller() {
        HttpResponse<ByteBuffer> byteBufferHttpResponse = rxHttpClient.exchange("/").blockingFirst();
        assert byteBufferHttpResponse.getStatus().getCode() == 200;
    }
}

它们都产生相同的响应。

希望有人知道如何使用 @MicronautTest 限制 bean 实例化的范围,否则我很可能会切换回 Spring boot

提前致谢

My question is how do I limit which controllers and singletons are loaded when testing Micronaut?

至少有 @Requires 注解允许在当前环境中加载 bean 时进行灵活配置。

例如,在您的情况下应该是这样的:

import javax.inject.Singleton;
import io.micronaut.context.annotation.Requires;

@Singleton
@Requires(notEnv = {Environment.TEST})
public class SingletonService {

    @EventListener
    public void init(ApplicationStartupEvent event) {
        throw new RuntimeException("I was initialized");
    }
}

因此,服务没有抛出异常。

但是,我真的不知道您为什么需要排除该服务的目的。其他方法可能更方便。

例如,请参阅 micronaut 测试文档中的 Mocking Collaborators 部分:https://micronaut-projects.github.io/micronaut-test/latest/guide/