在 Spock 集成测试中模拟 JPA 存储库
Mocking JPA repository in Spock integration tests
Spring 似乎在我的测试上下文中随机加载 bean。
我有以下存储库 类 User
:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findOneByExternalId(Long externalId);
Optional<User> findOneById(Long id);
}
它的等效测试:
@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;
}
}
}
Device
还有两个:
@Repository
public interface DeviceRepository extends JpaRepository<Device, Long> {
Optional<Device> findOneByDeviceId(String deviceId);
Optional<Device> findOneByDeviceIdAndToken(String deviceId, String token);
}
等效测试:
@Primary
@Repository(value = "deviceRepository")
public interface TestDeviceRepository extends DeviceRepository {
@Override
default <S extends Device> S save(S device) {
return device;
}
}
下面的测试通过了 UserRepository
,但未通过 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
}
}
可能是什么原因造成的?我遍历了 applicationContext
内容,它清楚地表明我的 UserRepository
bean 有一个名字 userRepository
而我的 DeviceRepository
有一个名字 deviceRepository
.
这是测试的输出:
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
最简单的解决方案有时是最难想到的。我承认是我想多了。
我所要做的就是使用 @Profile
注释,例如:
@Repository
@Profile("!test")
public interface DeviceRepository extends JpaRepository<Device, Long> {
/* ... implementation... */
}
并在测试中class
@Repository
@Profile("test")
public interface TestDeviceRepository extends DeviceRepository {
/* ... implementation... */
}
现在我所有的测试都通过了!
Spring 似乎在我的测试上下文中随机加载 bean。
我有以下存储库 类 User
:
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findOneByExternalId(Long externalId);
Optional<User> findOneById(Long id);
}
它的等效测试:
@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;
}
}
}
Device
还有两个:
@Repository
public interface DeviceRepository extends JpaRepository<Device, Long> {
Optional<Device> findOneByDeviceId(String deviceId);
Optional<Device> findOneByDeviceIdAndToken(String deviceId, String token);
}
等效测试:
@Primary
@Repository(value = "deviceRepository")
public interface TestDeviceRepository extends DeviceRepository {
@Override
default <S extends Device> S save(S device) {
return device;
}
}
下面的测试通过了 UserRepository
,但未通过 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
}
}
可能是什么原因造成的?我遍历了 applicationContext
内容,它清楚地表明我的 UserRepository
bean 有一个名字 userRepository
而我的 DeviceRepository
有一个名字 deviceRepository
.
这是测试的输出:
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
最简单的解决方案有时是最难想到的。我承认是我想多了。
我所要做的就是使用 @Profile
注释,例如:
@Repository
@Profile("!test")
public interface DeviceRepository extends JpaRepository<Device, Long> {
/* ... implementation... */
}
并在测试中class
@Repository
@Profile("test")
public interface TestDeviceRepository extends DeviceRepository {
/* ... implementation... */
}
现在我所有的测试都通过了!