Skip to the content.

使用 Grpc-Stubs 测试

<- 返回索引

本节描述如何为使用了 @GrpcClient 注解或 grpc stub 的组件编写测试用例。

目录

附加主题

前言

通常有两种方法来测试你包含 grpc stub 的组件:

注意:在测试期间,这两种形式有着非常明显的差异。 请仔细考虑每种形式中列出的利弊。

要测试的组件

让我们假设,我们希望测试以下组件:

@Component
public class MyComponent {

    private ChatServiceBlockingStub chatService;

    @GrpcClient("chatService")
    public void setChatService(ChatServiceBlockingStub chatService) {
        this.chatService = chatService;
    }

    public String sayHello(String name) {
        HelloRequest request = HelloRequest.newBuilder()
                .setName(name)
                .build();
        HelloReply reply = chatService.sayHello(name)
        return reply.getMessage();
    }

}

有用的依赖项

在您开始编写自己的测试框架之前,您可能想要使用以下库来使您的工作更加简单。

注意: Spring-Boot-Test已经包含一些依赖项,所以请确保您排除掉了冲突的版本。

对于Maven来说,添加以下依赖:

<!-- JUnit-Test-Framework -->
<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>
<!-- Grpc-Test-Support -->
<dependency>
    <groupId>io.grpc</groupId>
    <artifactId>grpc-testing</artifactId>
    <scope>test</scope>
</dependency>
<!-- Spring-Test-Support (Optional) -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<!-- Mocking Framework (Optional) -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-all</artifactId>
    <scope>test</scope>
</dependency>

Gradle 使用:

// JUnit-Test-Framework
testImplementation("org.junit.jupiter:junit-jupiter-api")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine")
// Grpc-Test-Support
testImplementation("io.grpc:grpc-testing")
// Spring-Test-Support (Optional)
testImplementation("org.springframework.boot:spring-boot-starter-test")
// Mocking Framework (Optional)
testImplementation("org.mockito:mockito-all")

使用 Mocked Stub

为了测试该方法,我们模拟 stub 并用 setter 注入它。

优点

缺点

实现

  1. 将 mockito 添加到 dependencies 中(见 上文 )
  2. 配置 mockito 使它与 final 修饰的类/方法一起工作

    为此,我们需要创建一个文件 src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker 包含:

    mock-maker-inline
    
  3. 像往常一样编写我们的 stub,并在测试过程中明确地将它设置到你的组件里

    public class MyComponentTest {
    
        private MyComponent myComponent = new MyComponent();
        private ChatServiceBlockingStub chatService = Mockito.mock(ChatServiceBlockingStub.class);
    
        @BeforeEach
        void setup() {
            myComponent.setChatService(chatService);
        }
    
        @Test
        void testSayHello() {
            Mockito.when(chatService.sayHello(...)).thenAnswer(...);
            assertThat(myComponent.sayHello("ThisIsMyName")).contains("ThisIsMyName");
        }
    
    }
    

运行一个虚拟服务

为了测试这个方法,我们自己启动了一个 grpc 服务端,并在测试中连接到它。

优点

缺点

实现

实际的使用方式像下面这样:

@SpringBootTest(properties = {
        "grpc.server.inProcessName=test", // Enable inProcess server
        "grpc.server.port=-1", // Disable external server
        "grpc.client.chatService.address=in-process:test" // Configure the client to connect to the inProcess server
        })
@SpringJUnitConfig(classes = { MyComponentIntegrationTestConfiguration.class })
// Spring doesn't start without a config (might be empty)
@DirtiesContext // Ensures that the grpc-server is properly shutdown after each test
        // Avoids "port already in use" during tests
public class MyComponentTest {

    @Autowired
    private MyComponent myComponent;

    @Test
    @DirtiesContext
    void testSayHello() {
        assertThat(myComponent.sayHello("ThisIsMyName")).contains("ThisIsMyName");
    }

}

所需的配置看起来像这样:

@Configuration
@ImportAutoConfiguration({
        GrpcServerAutoConfiguration.class, // Create required server beans
        GrpcServerFactoryAutoConfiguration.class, // Select server implementation
        GrpcClientAutoConfiguration.class}) // Support @GrpcClient annotation
public class MyComponentIntegrationTestConfiguration {

    @Bean
    MyComponent myComponent() {
        return new MyComponent();
    }

    @Bean
    ChatServiceImplForMyComponentIntegrationTest chatServiceImpl() {
        return new ChatServiceImplForMyComponentIntegrationTest();
    }

}

虚拟的服务可能看起来像这样:

@GrpcService
public class ChatServiceImplForMyComponentIntegrationTest extends ChatServiceGrpc.ChatServiceImplBase {

    @Override
    public void sayHello(HelloRequest request, StreamObserver<HelloReply> responseObserver) {
        HelloReply response = HelloReply.newBuilder()
                .setMessage("Hello ==> " + request.getName())
                .build();
        responseObserver.onNext(response);
        responseObserver.onComplete();
    }

    // Methods that aren't used in the test don't need to be implemented

}

附加主题


<- 返回索引