Jezor Jezor - 12 days ago 5
Groovy Question

Mocking JPA repository in Spock integration tests

Spring seems to load beans randomly in my test context.

I have following repository classes for

User
:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

User findOneByExternalId(Long externalId);

Optional<User> findOneById(Long id);

}


and it's test equivalent:

@Primary
@Repository(value = "userRepository")
public interface TestUserRepository extends UserRepository {

Long TEST_CORRECT_USER_ID = 1L;
Long TEST_INCORRECT_USER_ID = 2L;

@Override
default User findOneByExternalId(Long externalId) {
return getTestUser(externalId);
}

@Override
default Optional<User> findOneById(Long id) {
return ofNullable(getTestUser(id));
}

default User getTestUser(Long id) {
if (TEST_CORRECT_USER_ID.equals(id)) {
User user = new User();
user.setExternalId(id);
user.setDevices(emptyList());
user.setId(id);
return user;
}
else {
return null;
}
}

}


and two more for
Device
:

@Repository
public interface DeviceRepository extends JpaRepository<Device, Long> {

Optional<Device> findOneByDeviceId(String deviceId);

Optional<Device> findOneByDeviceIdAndToken(String deviceId, String token);

}


test equivalent:

@Primary
@Repository(value = "deviceRepository")
public interface TestDeviceRepository extends DeviceRepository {

@Override
default <S extends Device> S save(S device) {
return device;
}

}


the test below is passing for the
UserRepository
, but not for the
DeviceRepository
:

@SpringBootTest(classes = Application)
@ContextConfiguration(classes = [TestDeviceRepository, TestUserRepository])
class IntegrationContextSpecTest extends Specification {

@Autowired
ApplicationContext applicationContext

@Unroll("Bean #bean should be instance of #clazz")
def "should initialize test beans instead of normal beans"() {
expect:
clazz.isAssignableFrom(applicationContext.getBean(bean).
getClass())

where:
bean | clazz
UserRepository | TestUserRepository
DeviceRepository | TestDeviceRepository
}

}


What might be the cause of it? I iterated over
applicationContext
contents and it clearly says that my
UserRepository
bean have a name
userRepository
and my
DeviceRepository
have a name
deviceRepository
.

Here's the output from test:

Condition not satisfied:

clazz.isAssignableFrom(applicationContext.getBean(bean).getClass())
| | | | | |
| false | | | class com.sun.proxy.$Proxy143
| | | interface com.example.repository.DeviceRepository
| | org.springframework.data.jpa.repository.support.SimpleJpaRepository@10e4cf09
| org.springframework.web.context.support.GenericWebApplicationContext@7dc51783: startup date [Sun Nov 27 22:09:16 CET 2016]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@56aaaecd
interface com.example.test.integration.mock.TestDeviceRepository

Answer

The simplest solutions are sometimes the most difficult to think of. I admit that I was overthinking the problem too much.

All I had to do is use @Profile annotation like:

@Repository
@Profile("!test")
public interface DeviceRepository extends JpaRepository<Device, Long> { 
    /* ... implementation... */ 
}

and in the test class

@Repository
@Profile("test")
public interface TestDeviceRepository extends DeviceRepository { 
    /* ... implementation... */ 
}

And now all my tests are passing!