A very beginner problem but one I can't get past. I have a basic Spring Boot app, and one Spring Data MongoDB repository that connects to the cloud atlas instance. The problem is that in my Spring Boot test, my repository is not autowired and the embedded MongoDB instance is not created. If I start the Spring Boot app and in the main class I autowire the repository, that works. Why does it not work in my test?
This is my test class:
@DataMongoTest
@ExtendWith(SpringExtension.class)
public class SampleServiceTest{
@Autowired
private SampleRepository sampleRepository;
@Test
public void shouldCreateSample(){
sampleRepository.save(new Sample());
}
}
This is my pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath></relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.comand</groupId>
<artifactId>business-owner-service</artifactId>
<version>1.0-SNAPSHOT</version>
<description>API Gateway</description>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Greenwich.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
When you use the @Autowired
annotation, basically you are mapping the variable with an object that exists in the application context. Remember the application context is created when you start the spring boot application. All classes that have the annotation @Service
, @Repository
, @Component
are instantiated in the application context.
I assume that SampleRepository
has one of the following annotations :@Service
, @Repository
, @Component
@Repository
. And when you start the spring boot application, the application context is created and has instantiated the SampleRepository
class.
The @Autowire
annotation will map the object that is created in the application context with the variable that has the annotation @Autowire
.
The reason why It doesn't work in your test is that the object of the SampleRepository
class does not exist. And you cannot map it to the variable that you have annotated with @Autowire
.
You can fix this in two ways:
@EnableConfigurationProperties(SampleRepository.class)
public class TestConfiguration {
}
@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = { TestConfiguration.class })
public class SampleServiceTest{
}
@DataMongoTest
like below:@DataMongoTest(includeFilters = @Filter(Service.class))
//or
@DataMongoTest(includeFilters = @Filter(Component.class))
Using @DataMongoTest
annotation will disable full auto-configuration and instead apply only configuration relevant to MongoDB tests. So the classes annotated with @Services
,Component
are not instantiated. includeFilters
are a set of filters that can be used to add filtered beans to the application context.
I suspect that you have annotated the class SampleRepository
with a @Service
or @Component
annotation and that's why it did not create the instance of SampleRepository
class.
I looked your code in git repo, and modified as below:
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class BusinessOwnerServiceTest {
@Autowired
private BusinessOwnerService businessOwnerService;
@Autowired
private BusinessOwnerRepository businessOwnerRepository;
@Test
public void shouldCreateNewBusinessOwner(){
businessOwnerService.findBusinessOwnerByEmail("EMAIL@gmail.com");
}
}
Below it is the second solution:
@RunWith(SpringJUnit4ClassRunner.class)
@DataMongoTest(includeFilters = @Filter(Service.class))
public class BusinessOwnerServiceTest {
@Autowired
private BusinessOwnerService businessOwnerService;
@Autowired
private BusinessOwnerRepository businessOwnerRepository;
@Test
public void shouldCreateNewBusinessOwner(){
businessOwnerService.findBusinessOwnerByEmail("EMAIL@gmail.com");
}
}