@Autowired поле NULL при выполнении производственного кода через интеграционные тесты - PullRequest
0 голосов
/ 12 февраля 2020

Немного странно, что я почесал голову за последние несколько дней. У меня есть репозиторий JPA, который вводится в поле класса обслуживания. Отлично работает при запуске сервера и отправке запроса через клиента, но когда код выполняется с помощью интеграционных тестов, класс внедренного поля (CustomerRepository) всегда равен null.

Я пробовал различные советы через inte rnet но я не нашел аналогичного сценария для моего, любая помощь будет высоко ценится

Класс обслуживания

@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);
    }

}

Ответы [ 3 ]

0 голосов
/ 12 февраля 2020

Трудно ответить, не имея возможности прочитать трассировки стека. Я бы начал исследовать эту проблему, посмотрев на классы конфигурации весеннего контекста. Возможно, некоторые из них отсутствуют во время выполнения вашего интеграционного теста.

Если в вашем приложении существуют пружинные @Configuration или другие @Component классы, это может помочь явно загрузить их вместе с вашими тестами неожиданно нулевое значение CustomerRepository:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {AppConfig.class, CustomerRepository.class })
public class CustomerServiceIT {

Возможно, это не решит проблему, но, возможно, обнаружит некоторые сообщения об ошибках, которые помогут вам исследовать проблему.

0 голосов
/ 12 февраля 2020

В тесте вы вызываете new CustomerService(). Вы создаете объект самостоятельно, а не через пружину. Я полагаю, вам следует создать поле в классе теста

@Autowired private final CustomerService customerService

и передать его в grpcCleanup.register(InProcessServerBuilder .forName(serverName).directExecutor().addService(customerService).build().start());

0 голосов
/ 12 февраля 2020

Вы можете использовать Mockito или любую другую платформу для тестирования, чтобы смоделировать ваши классовые зависимости (ваш сервис и ваш репозиторий JPA).

Вы можете использовать эти функции:

  • @InjectMocks - создает экземпляр объекта тестирования и пытается вставить поля, помеченные @Mock или @Spy, в частные поля объекта тестирования
  • @Mock - создает макетный экземпляр поля, которое он аннотирует

В вашем тестовом классе вы должны использовать @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 поддельные данные для целей тестирования.

Примечание: : Приятно иметь соглашение об именах классов и особенно тестовых классов. Обычное использование - всегда давать суффикс xxTest, как я сделал в ответе

...