Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Springboot: How can I perform an integration test with real dependencies?

I'm starting to learn both Java and Spring boot now, and I'm having some problems with dependency injection in integration tests. I have a class under src/main/java/com/rfd/domain/services called TransactionService, which is marked as @Service and which has another dependencies, one of them a repository created by Spring boot. When I launch the application, it is launched correctly so I assume the dependencies are being resolved correctly. This is the summarized class:

package com.rfd.domain.services;

import allNeededImports

@Service
public class TransactionsService {

    @Autowired
    private KambiTransactionRepository kambiTransactionRepository;

    @Autowired
    private TransactionFactory transactionFactory;

    public List<Transaction> retrieveTransactions(String couponExternalId) throws InvalidTransactionException {
        // someCode
    }
}

and now, I have a TransactionsServiceTests class under /src/test/java/com/rfd/integrationtests/domain/services:

package com.rfd.integrationtests.domain.services;
import allNeededImports

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Main.class)
@DataMongoTest
@TestPropertySource(locations = "classpath:application-integrationtest.properties")
public class TransactionsServiceTests {

    @Autowired
    private TransactionsService transactionsService;

    @Test
    public void retrieveTransactions_happyPathMultipleTransactions_transactionsRetrieved() throws InvalidTransactionException {
        // test code
    }

When I try to launch the tests, I receive the following error:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.rfd.domain.services.TransactionsService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

I have tried to create my own @TestConfiguration class, in which I create a method marked with @Bean and returning a new instance of TransactionService, and it works. However, the error now is for the KambiTransactionRepository dependency, and I don't have an implementation of it because it is given by spring boot:

package com.rfd.infrastructure.repositories;

import com.rfd.infrastructure.models.KambiTransaction;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface KambiTransactionRepository extends MongoRepository<KambiTransaction, String> {

    List<KambiTransaction> findByCouponRef(String couponRef);
}

QUESTION How can I execute the integration test using the dependency resolution of the main code?

like image 267
DarthRoman Avatar asked Jan 03 '23 13:01

DarthRoman


2 Answers

As @M.Deinum remarked in comments, @SpringBootTest and @DataMongoTest are mutually exclusive, so removing @DataMongoTest solved the problem.

However, if you still want to use the @DataMongoTest annotation, you can use this sentence:

@DataMongoTest(includeFilters = @ComponentScan.Filter(Service.class))

That way, all classes that are annotated with @Component will be loaded and autowired. This includes (among others) @Service, @Repository and @Controller.

like image 50
DarthRoman Avatar answered Jan 06 '23 01:01

DarthRoman


I needed to add test for my storage service and I needed to autowire MongoRepository. The answer to this question did not solve my problem. However I found a solution and will share here in case someone else needs. I solved it as following:

Repository:

@Repository
public interface MyRepository extends MongoRepository<MyEntity, String> {
}

Entity:

@Document(collection = "my_entity")
public class MyEntity {
    @Id
    private String id;
}

Service:

@Service
public class StorageServiceImpl implements StorageService {
    private final MyRepository repository;

    public StorageServiceImpl(MyRepository repository) {
        this.repository = repository;
    }
...
}

Test:

@Import(ObjectMapperConfiguration.class)
@DataMongoTest
class StorageServiceImplTest {

    @Autowired
    private MyRepository repository;

    private StorageService storageService;

    @BeforeEach
    void setup() {
        storageService = new StorageServiceImpl(repository);
        var entity = new MyEntity();
        repository.save(entity);
    }

    @Test
    void create() {
        assertEquals(1, repository.count());
        var entity = storageService.create();
        assertEquals(2, repository.count());
    }
...
}

Test Config:

@TestConfiguration
public class ObjectMapperConfiguration {
    @Bean
    public ObjectMapper objectMapper() {
        return new ObjectMapper();
    }
}

Test Application

@SpringBootApplication
public class TestApplication {
}
like image 20
Gokhan Celikkaya Avatar answered Jan 06 '23 01:01

Gokhan Celikkaya