ฉันต้องการพื้นที่เก็บข้อมูล (พูดUserRepository
) ที่สร้างขึ้นด้วยความช่วยเหลือของ Spring Data ฉันยังใหม่กับ Spring-data (แต่ไม่ใช่ฤดูใบไม้ผลิ) และฉันใช้บทช่วยสอนนี้ ตัวเลือกของเทคโนโลยีสำหรับจัดการกับฐานข้อมูลคือ JPA 2.1 และ Hibernate ปัญหาคือฉันไม่รู้วิธีเขียนการทดสอบหน่วยสำหรับที่เก็บดังกล่าว
ลองใช้create()
วิธีการเช่น ในขณะที่ฉันกำลังทำการทดสอบก่อนฉันควรจะเขียนการทดสอบหน่วยสำหรับมัน - และนั่นคือสิ่งที่ฉันพบปัญหาสามประการ:
ขั้นแรกฉันจะฉีดเลียนแบบ
EntityManager
การใช้งานUserRepository
อินเทอร์เฟซที่ไม่มีอยู่ได้อย่างไร Spring Data จะสร้างการใช้งานตามอินเทอร์เฟซนี้:public interface UserRepository extends CrudRepository<User, Long> {}
อย่างไรก็ตามฉันไม่รู้ว่าจะบังคับให้ใช้การ
EntityManager
จำลองและการล้อเลียนอื่น ๆ อย่างไร - ถ้าฉันเขียนการใช้งานด้วยตัวเองฉันอาจมีวิธีการตั้งค่าเพื่อEntityManager
ให้ฉันใช้การจำลองสำหรับการทดสอบหน่วย (ในฐานะที่เป็นสำหรับการเชื่อมต่อฐานข้อมูลจริงฉันมีJpaConfiguration
ระดับกำกับด้วย@Configuration
และ@EnableJpaRepositories
ที่โปรแกรมกำหนดถั่วDataSource
,EntityManagerFactory
,EntityManager
ฯลฯ - แต่ที่เก็บควรจะทดสอบที่เป็นมิตรและอนุญาตให้มีการเอาชนะสิ่งเหล่านี้)ประการที่สองฉันควรทดสอบการโต้ตอบหรือไม่? มันยากสำหรับฉันที่จะเข้าใจว่าวิธีการใด
EntityManager
และQuery
ควรจะเรียกว่าอะไร (คล้าย ๆ กันverify(entityManager).createNamedQuery(anyString()).getResultList();
) เนื่องจากฉันไม่ใช่คนที่เขียนการนำไปใช้ประการที่สามฉันควรจะทดสอบหน่วยวิธีการสร้างข้อมูลโดยสปริงตั้งแต่แรกหรือไม่? ดังที่ฉันทราบรหัสไลบรารีของบุคคลที่สามไม่ควรผ่านการทดสอบหน่วย - เฉพาะโค้ดที่นักพัฒนาเขียนขึ้นเองเท่านั้นที่ควรได้รับการทดสอบหน่วย แต่ถ้าเป็นเรื่องจริงก็ยังคงนำคำถามแรกกลับมาที่เกิดเหตุ: พูดว่าฉันมีวิธีการที่กำหนดเองสองสามวิธีสำหรับที่เก็บของฉันซึ่งฉันจะเขียนการนำไปใช้ฉันจะฉีดล้อเลียนของฉัน
EntityManager
และQuery
ในขั้นสุดท้ายสร้างได้อย่างไร ที่เก็บ?
หมายเหตุ: ฉันจะทดสอบการขับที่เก็บของฉันโดยใช้ทั้งการรวมและการทดสอบหน่วย สำหรับการทดสอบการรวมของฉันฉันใช้ฐานข้อมูลในหน่วยความจำ HSQL และเห็นได้ชัดว่าฉันไม่ได้ใช้ฐานข้อมูลสำหรับการทดสอบหน่วย
และอาจเป็นคำถามที่สี่การทดสอบการสร้างกราฟวัตถุและการดึงกราฟวัตถุที่ถูกต้องในการทดสอบการรวม (พูดว่าฉันมีกราฟวัตถุที่ซับซ้อนที่กำหนดด้วยไฮเบอร์เนต) หรือไม่
อัปเดต: วันนี้ฉันได้ทดลองใช้การฉีดจำลองอย่างต่อเนื่อง - ฉันได้สร้างคลาสชั้นในแบบคงที่เพื่อให้สามารถฉีดจำลองได้
@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());
}
}
อย่างไรก็ตามการเรียกใช้การทดสอบนี้ทำให้ฉันมี stacktrace ต่อไปนี้:
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