lundi 30 mai 2016

spring-boot & junit: how to mock CRUD repositories when using @ComponentScan?

I'm trying to junit test a spring boot application. For this question, I have created a simple example that serves to illustrate my issue.

A service bean:

@Service
public class DummyService {
    public String dummyMethod(String str) {
        return "Dummy(" + str + ")";
    }
}

an entity class:

@Entity
public class KeyValue {

    @Id
    @GeneratedValue
    Long id;

    @Column(nullable = false)
    String key;

    @Column(nullable = false)
    String value;
}

and a repository:

public interface KeyValueRepository extends CrudRepository<KeyValue, Long>
{
}

All of these are @Autowired into a controller.

For Unit testing, I have created a Configuration class:

@Configuration
public class MyTestConfig {

    @Bean
    public DummyService dummyService() {
        return new DummyService();
    }

    @Bean
    public KeyValueRepository keyValueRepository() {
        return  Mockito.mock(KeyValueRepository.class);
    }
}

If I test it now, it works just perfect:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = MyTestConfig.class)
public class UsingMockTest {

    @Autowired
    KeyValueRepository keyValueRepository;

    @Test
    public void respositoryIsMock()
    {
        MockUtil mo = new MockUtil();
        assertTrue(mo.isMock(keyValueRepository));
    }
}

This gives me a mocked version of the repository, as expected.

However, it turns out that this approach is not suitable for a larger application. Our real application contains 50+ beans which are all autowired. So what I would like to do is to @ComponentScan the application and then, in my configuration only override the beans I'd actually want to mock.

However, if I set up the test configuration like this:

@Configuration
@ComponentScan(
        basePackages = {"com.example"}
)
public class MyTestConfig {

    @Bean
    public DummyService thisForThatService() {
        return new DummyService();
    }

    @Bean
    public KeyValueRepository sayingRepository() {
        return Mockito.mock(KeyValueRepository.class);
    }
}

I will get a failed test because the repository is no longer a mock object. When I look at the spring boot log, I see the following:

2016-05-30 15:36:50.539  INFO 5908 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'dummyService' with a different definition: replacing [Generic bean: class [com.example.DummyService]; scope=singleton; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null; defined in file [C:\work\dev\testDemo\build\classes\main\com\example\DummyService.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=myTestConfig; factoryMethodName=dummyService; initMethodName=null; destroyMethodName=(inferred); defined in com.example.MyTestConfig]
2016-05-30 15:36:50.658  INFO 5908 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'keyValueRepository' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=myTestConfig; factoryMethodName=keyValueRepository; initMethodName=null; destroyMethodName=(inferred); defined in com.example.MyTestConfig] with [Root bean: class [org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null]

So, for the simple "DummyBean", the scanned configuration is overridden by my explicit configuration in the MyTestConfig class (which is exactly what I want), but for the CRUD Repository, the mock bean from the MyTestConfig class is overridden by the component-scanned JPA bean.

How do I avoid this behaviour in order to be able to inject @Autowired mock CRUD repositories?

Any help is greatly appreciated!

Aucun commentaire:

Enregistrer un commentaire