0

I have some Repository-beans in my project. I wire the collection of this repos to some of my services and iteratively fetch data from each of them. But in my unit-tests I want to exclude all of these repositories (real implementations) from Spring context and use only fake one. How do I achieve it?

Dmytro Titov
  • 2,802
  • 6
  • 38
  • 60

3 Answers3

0

Testing the Auto-Configuration:

Let’s create a very simple example to test our auto-configuration. We will create an entity class called MyUser, and a MyUserRepository interface using Spring Data:

@Entity
public class MyUser {
    @Id
    private String email;

    // standard constructor, getters, setters
}

public interface MyUserRepository 
  extends JpaRepository<MyUser, String> { }

To enable auto-configuration, we can use one of the @SpringBootApplication or @EnableAutoConfiguration annotations:

@SpringBootApplication
public class AutoconfigurationApplication {
    public static void main(String[] args) {
        SpringApplication.run(AutoconfigurationApplication.class, args);
    }
}

Next, let’s write a JUnit test that saves a MyUser entity:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(
  classes = AutoconfigurationApplication.class)
@EnableJpaRepositories(
  basePackages = { "com.baeldung.autoconfiguration.example" })
public class AutoconfigurationTest {

    @Autowired
    private MyUserRepository userRepository;

    @Test
    public void whenSaveUser_thenOk() {
        MyUser user = new MyUser("user@email.com");
        userRepository.save(user);
    }
}

Since we have not defined our DataSource configuration, the application will use the auto-configuration we have created to connect to a MySQL database called myDb.

The connection string contains the createDatabaseIfNotExist=true property, so the database does not need to exist. However, the user mysqluser or the one specified through the mysql.user property if it is present, needs to be created.

We can check the application log to see that the MySQL data source is being used:

web - 2017-04-12 00:01:33,956 [main] INFO  o.s.j.d.DriverManagerDataSource - Loaded JDBC driver: com.mysql.cj.jdbc.Driver

Disabling Auto-Configuration Classes

If we wanted to exclude the auto-configuration from being loaded, we could add the @EnableAutoConfiguration annotation with exclude or excludeName attribute to a configuration class:

@Configuration
@EnableAutoConfiguration(
  exclude={MySQLAutoconfiguration.class})
public class AutoconfigurationApplication {
    //...
}

Another option to disable specific auto-configurations is by setting the spring.autoconfigure.exclude property:

spring.autoconfigure.exclude=com.baeldung.autoconfiguration.MySQLAutoconfiguration

Resource Link: Create a Custom Auto-Configuration with Spring Boot

Solution#2:

Matt C also given a solution in this tutorial:How to exclude *AutoConfiguration classes in Spring Boot JUnit tests?.

SkyWalker
  • 28,384
  • 14
  • 74
  • 132
0

Spring context is really rarely used in unit testing. First question that you should ask yourself: "What unit of work I want to test?". After you get an answer, you should test it in isolation. You don't need spring context here. Spring context usually needed in integration tests. Unit tests must not depend on context.

You could use @Mock and @InjectMocks annotations, provided by Mockito. For example:

public class SomeServiceTest {

    @InjectMocks
    SomeService someService;

    @Mock
    PersonRepository personRepository;

    @Before
    public void setUp(){
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testFindPersonExpectOk(){
        String name = "John";
        String secondName = "Doe";

        // Configure the behavior for mocked repository
        Person person = new Person(name, secondName);
        when(personRepository.findByNameAndSecondName(name, 
            secondName).thenReturn(person);

        // Call some method in testing service
        Person fetchedPerson = someService.findUser(name, secondName);

        // Check that everything is fine
        assertThat(fetchedPerson.getName, is(name));

    }
}
Nikita Klimov
  • 299
  • 1
  • 6
0

Here's the solution:

@SpringBootApplication
@ComponentScan(basePackages = "my.base.package",
        excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = "my.base.package.repositories.*.*"))
public class TestApplication {

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }

    @Bean
    public Repository someFakeRepository() {
        return new FakeRepository();
    }

}

And then the test-class:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TestApplication.class)
public class ApplicationTests {

    @Autowired
    private Repository fakeRepository;

    // tests

}

So I've replaced all real beans with the test-one.

Dmytro Titov
  • 2,802
  • 6
  • 38
  • 60