Tôi muốn một kho lưu trữ (giả sử UserRepository
) được tạo ra với sự trợ giúp của Spring Data. Tôi chưa quen với dữ liệu mùa xuân (nhưng không phải là mùa xuân) và tôi sử dụng hướng dẫn này . Sự lựa chọn công nghệ của tôi để xử lý cơ sở dữ liệu là JPA 2.1 và Hibernate. Vấn đề là tôi không biết cách viết bài kiểm tra đơn vị cho một kho lưu trữ như vậy.
Hãy lấy create()
phương pháp làm ví dụ. Khi tôi đang làm việc thử nghiệm trước tiên, tôi phải viết một bài kiểm tra đơn vị cho nó - và đó là nơi tôi gặp phải ba vấn đề:
Đầu tiên, làm thế nào để tôi đưa một mock of an
EntityManager
vào việc triển khaiUserRepository
giao diện không tồn tại ? Spring Data sẽ tạo ra một triển khai dựa trên giao diện này:public interface UserRepository extends CrudRepository<User, Long> {}
Tuy nhiên, tôi không biết làm thế nào để buộc nó sử dụng một bản
EntityManager
giả và các bản giả khác - nếu tôi đã tự viết bản thực hiện, tôi có thể có một phương thức setter choEntityManager
phép tôi sử dụng bản giả cho bài kiểm tra đơn vị. (Đối với khả năng kết nối cơ sở dữ liệu thực tế, tôi có mộtJpaConfiguration
lớp, chú thích với@Configuration
và@EnableJpaRepositories
, mà lập trình định nghĩa đậu choDataSource
,EntityManagerFactory
,EntityManager
vv - nhưng kho nên kiểm tra thân thiện và cho phép trọng những điều này).Thứ hai, tôi có nên kiểm tra các tương tác không? Thật khó cho tôi để tìm ra phương pháp nào
EntityManager
vàQuery
được gọi là (gần giống với phương pháp đóverify(entityManager).createNamedQuery(anyString()).getResultList();
), vì đó không phải là tôi, người đang viết bài thực hiện.Thứ ba, tôi có nên kiểm tra đơn vị các phương thức tạo dữ liệu mùa xuân ở vị trí đầu tiên không? Như tôi biết, mã thư viện của bên thứ ba không được coi là đã được kiểm tra đơn vị - chỉ có mã mà các nhà phát triển tự viết được cho là được kiểm tra đơn vị. Nhưng nếu đó là sự thật, nó vẫn đưa câu hỏi đầu tiên trở lại hiện trường: giả sử, tôi có một vài phương thức tùy chỉnh cho kho lưu trữ của mình, mà tôi sẽ viết bằng cách thực hiện, làm thế nào để tôi đưa bản giả của mình vào
EntityManager
vàQuery
cuối cùng, được tạo ra kho?
Lưu ý: Tôi sẽ lái thử các kho lưu trữ của mình bằng cả tích hợp và kiểm tra đơn vị. Đối với các bài kiểm tra tích hợp của tôi, tôi đang sử dụng cơ sở dữ liệu trong bộ nhớ HSQL và rõ ràng tôi không sử dụng cơ sở dữ liệu cho các bài kiểm tra đơn vị.
Và có lẽ là câu hỏi thứ tư, có đúng không khi kiểm tra việc tạo biểu đồ đối tượng chính xác và truy xuất biểu đồ đối tượng trong các thử nghiệm tích hợp (giả sử, tôi có một biểu đồ đối tượng phức tạp được xác định bằng Hibernate)?
Cập nhật: hôm nay tôi đã tiếp tục thử nghiệm tiêm giả - Tôi đã tạo một lớp bên trong tĩnh để cho phép tiêm giả.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
@TransactionConfiguration(defaultRollback = true)
public class UserRepositoryTest {
@Configuration
@EnableJpaRepositories(basePackages = "com.anything.repository")
static class TestConfiguration {
@Bean
public EntityManagerFactory entityManagerFactory() {
return mock(EntityManagerFactory.class);
}
@Bean
public EntityManager entityManager() {
EntityManager entityManagerMock = mock(EntityManager.class);
//when(entityManagerMock.getMetamodel()).thenReturn(mock(Metamodel.class));
when(entityManagerMock.getMetamodel()).thenReturn(mock(MetamodelImpl.class));
return entityManagerMock;
}
@Bean
public PlatformTransactionManager transactionManager() {
return mock(JpaTransactionManager.class);
}
}
@Autowired
private UserRepository userRepository;
@Autowired
private EntityManager entityManager;
@Test
public void shouldSaveUser() {
User user = new UserBuilder().build();
userRepository.save(user);
verify(entityManager.createNamedQuery(anyString()).executeUpdate());
}
}
Tuy nhiên, việc chạy thử nghiệm này mang lại cho tôi stacktrace sau:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.DefaultTestContext.getApplicationContext(DefaultTestContext.java:101)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:109)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:319)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:212)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:232)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:89)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:175)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:77)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userRepository': Error setting property values; nested exception is org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'entityManager' threw exception; nested exception is java.lang.IllegalArgumentException: JPA Metamodel must not be null!
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1493)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1197)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:684)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:760)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:121)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:250)
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContextInternal(CacheAwareContextLoaderDelegate.java:64)
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:91)
... 28 more
Caused by: org.springframework.beans.PropertyBatchUpdateException; nested PropertyAccessExceptions (1) are:
PropertyAccessException 1: org.springframework.beans.MethodInvocationException: Property 'entityManager' threw exception; nested exception is java.lang.IllegalArgumentException: JPA Metamodel must not be null!
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:108)
at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:62)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1489)
... 44 more