通过集成测试执行生产代码时@Autowired 字段为空
@Autowired field null when executing production code via integration tests
过去几天我一直在摸不着头脑的一个奇怪的问题。我有一个现场注入到服务 class 中的 JPA 存储库。当 运行 服务器并通过客户端发送请求但是当通过集成测试执行代码时,注入的字段 class (CustomerRepository) 始终为空。
我已经通过互联网尝试了各种建议,但我没有找到与我相似的情况,任何帮助将不胜感激
服务class
@GRpcService
public class CustomerService extends CustomerServiceGrpc.CustomerServiceImplBase {
@Autowired
private CustomerRepository repository;
@Override
public void createCustomer(CreateCustomerRequest request, StreamObserver<CreateCustomerResponse> responseObserver) {
final CustomerDao convertedDao = ProtoToDaoConverter.convertCustomerRequestProtoToCustomerDao(request);
repository.save(convertedDao);
responseObserver.onNext(CreateCustomerResponse.newBuilder().setSuccess(true).build());
responseObserver.onCompleted();
}
}
集成测试
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class CustomerServiceIT {
@Rule
private final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
@Test
public void something() throws IOException {
String serverName = InProcessServerBuilder.generateName();
// Create a server, add service, start, and register for automatic graceful shutdown.
grpcCleanup.register(InProcessServerBuilder
.forName(serverName).directExecutor().addService(new CustomerService()).build().start());
customerServiceGrpc.CustomerServiceBlockingStub blockingStub = CustomerServiceGrpc.newBlockingStub(
// Create a client channel and register for automatic graceful shutdown.
grpcCleanup.register(InProcessChannelBuilder.forName(serverName).directExecutor().build()));
final CreateCustomerRequest request = CreateCustomerRequest.newBuilder().setFirstName("Simon").setSecondName("Brown").setRole("Product Developer").build();
final CreateCustomerResponse response = blockingStub.createCustomer(request);
}
}
您可以使用 Mockito 或任何其他测试框架来模拟您的 class 依赖项(您的服务和 JPA 存储库)。
您可以使用这些功能:
@InjectMocks
- 实例化测试对象实例并尝试将用 @Mock
或 @Spy
注释的字段注入到测试对象的私有字段中
@Mock
- 创建它注释的字段的模拟实例
在你的测试中 class 你必须使用 @Mock
来注入“一个假的存储库”:
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class CustomerServiceTest {
@InjectMocks
private CustomerService testingObject;
@Mock
private CustomerRepository customRepository;
@Rule
private final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
@BeforeMethod
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
@Test
public void something() throws IOException {
// inside the testing method you have to define what you mocked object should return.
// it means mocking the CustomRepository methods
CustomerDao customer = new CustomerDao(); // fill it with data
Mockito.when(customRepository.save()).thenReturn(customer);
// Create a server, add service, start, and register for automatic graceful shutdown.
grpcCleanup.register(InProcessServerBuilder
.forName(serverName).directExecutor().addService(new CustomerService()).build().start());
customerServiceGrpc.CustomerServiceBlockingStub blockingStub = CustomerServiceGrpc.newBlockingStub(
// Create a client channel and register for automatic graceful shutdown.
grpcCleanup.register(InProcessChannelBuilder.forName(serverName).directExecutor().build()));
final CreateCustomerRequest request = CreateCustomerRequest.newBuilder().setFirstName("Simon").setSecondName("Brown").setRole("Product Developer").build();
final CreateCustomerResponse response = blockingStub.createCustomer(request);
}
}
由于您的存储库及其方法是模拟的,因此您的 customerService 应该填充虚假数据以用于测试目的。
注意: :很高兴为 classes 制定命名约定,尤其是测试 classes。一个常见的用法是总是给 xxTest 的后缀,就像我在答案中所做的那样
如果无法读取堆栈跟踪,则很难回答。我会通过查看 spring 上下文配置 类 来开始调查这个问题。也许其中一些在您的集成测试运行时丢失了。
如果您的应用程序中存在 spring @Configuration
或其他 @Component
类,可能有助于在您的测试中显式加载它们,以及您意外的 null-值 CustomerRepository
:
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {AppConfig.class, CustomerRepository.class })
public class CustomerServiceIT {
这可能无法解决问题,但可能会揭示一些有助于您调查问题的错误消息。
在测试中调用 new CustomerService()
。您自己创建一个对象,而不是通过 spring。我猜你应该在 test class
中创建一个字段
@Autowired private final CustomerService customerService
并传入
grpcCleanup.register(InProcessServerBuilder
.forName(serverName).directExecutor().addService(customerService).build().start());
过去几天我一直在摸不着头脑的一个奇怪的问题。我有一个现场注入到服务 class 中的 JPA 存储库。当 运行 服务器并通过客户端发送请求但是当通过集成测试执行代码时,注入的字段 class (CustomerRepository) 始终为空。
我已经通过互联网尝试了各种建议,但我没有找到与我相似的情况,任何帮助将不胜感激
服务class
@GRpcService
public class CustomerService extends CustomerServiceGrpc.CustomerServiceImplBase {
@Autowired
private CustomerRepository repository;
@Override
public void createCustomer(CreateCustomerRequest request, StreamObserver<CreateCustomerResponse> responseObserver) {
final CustomerDao convertedDao = ProtoToDaoConverter.convertCustomerRequestProtoToCustomerDao(request);
repository.save(convertedDao);
responseObserver.onNext(CreateCustomerResponse.newBuilder().setSuccess(true).build());
responseObserver.onCompleted();
}
}
集成测试
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class CustomerServiceIT {
@Rule
private final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
@Test
public void something() throws IOException {
String serverName = InProcessServerBuilder.generateName();
// Create a server, add service, start, and register for automatic graceful shutdown.
grpcCleanup.register(InProcessServerBuilder
.forName(serverName).directExecutor().addService(new CustomerService()).build().start());
customerServiceGrpc.CustomerServiceBlockingStub blockingStub = CustomerServiceGrpc.newBlockingStub(
// Create a client channel and register for automatic graceful shutdown.
grpcCleanup.register(InProcessChannelBuilder.forName(serverName).directExecutor().build()));
final CreateCustomerRequest request = CreateCustomerRequest.newBuilder().setFirstName("Simon").setSecondName("Brown").setRole("Product Developer").build();
final CreateCustomerResponse response = blockingStub.createCustomer(request);
}
}
您可以使用 Mockito 或任何其他测试框架来模拟您的 class 依赖项(您的服务和 JPA 存储库)。
您可以使用这些功能:
@InjectMocks
- 实例化测试对象实例并尝试将用@Mock
或@Spy
注释的字段注入到测试对象的私有字段中@Mock
- 创建它注释的字段的模拟实例
在你的测试中 class 你必须使用 @Mock
来注入“一个假的存储库”:
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class CustomerServiceTest {
@InjectMocks
private CustomerService testingObject;
@Mock
private CustomerRepository customRepository;
@Rule
private final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
@BeforeMethod
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
@Test
public void something() throws IOException {
// inside the testing method you have to define what you mocked object should return.
// it means mocking the CustomRepository methods
CustomerDao customer = new CustomerDao(); // fill it with data
Mockito.when(customRepository.save()).thenReturn(customer);
// Create a server, add service, start, and register for automatic graceful shutdown.
grpcCleanup.register(InProcessServerBuilder
.forName(serverName).directExecutor().addService(new CustomerService()).build().start());
customerServiceGrpc.CustomerServiceBlockingStub blockingStub = CustomerServiceGrpc.newBlockingStub(
// Create a client channel and register for automatic graceful shutdown.
grpcCleanup.register(InProcessChannelBuilder.forName(serverName).directExecutor().build()));
final CreateCustomerRequest request = CreateCustomerRequest.newBuilder().setFirstName("Simon").setSecondName("Brown").setRole("Product Developer").build();
final CreateCustomerResponse response = blockingStub.createCustomer(request);
}
}
由于您的存储库及其方法是模拟的,因此您的 customerService 应该填充虚假数据以用于测试目的。
注意: :很高兴为 classes 制定命名约定,尤其是测试 classes。一个常见的用法是总是给 xxTest 的后缀,就像我在答案中所做的那样
如果无法读取堆栈跟踪,则很难回答。我会通过查看 spring 上下文配置 类 来开始调查这个问题。也许其中一些在您的集成测试运行时丢失了。
如果您的应用程序中存在 spring @Configuration
或其他 @Component
类,可能有助于在您的测试中显式加载它们,以及您意外的 null-值 CustomerRepository
:
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {AppConfig.class, CustomerRepository.class })
public class CustomerServiceIT {
这可能无法解决问题,但可能会揭示一些有助于您调查问题的错误消息。
在测试中调用 new CustomerService()
。您自己创建一个对象,而不是通过 spring。我猜你应该在 test class
@Autowired private final CustomerService customerService
并传入
grpcCleanup.register(InProcessServerBuilder
.forName(serverName).directExecutor().addService(customerService).build().start());