title: How to test Spring Data repositories?
body: <p>I want a repository (say, <code>UserRepository</code>) created with the help of Spring Data. I am new to spring-data (but not to spring) and I use this <a href="http://spring.io/guides/tutorials/data/3/">tutorial</a>. My choice of technologies for dealing with the database is JPA 2.1 and Hibernate. The problem is that I am clueless as to how to write unit tests for such a repository.</p>
  
  <p>Let's take <code>create()</code> method for instance. As I am working test-first, I am supposed to write a unit test for it - and that's where I bump into three problems:</p>
  
  <ul>
  <li><p>First, how do I inject a mock of an <code>EntityManager</code> into the non-existing implementation of a <code>UserRepository</code> interface? Spring Data would generate an implementation based on this interface:</p>
  
  <pre><code>public interface UserRepository extends CrudRepository&lt;User, Long&gt; {}
  </code></pre>
  
  <p>However, I don't know how to force it to use an <code>EntityManager</code> mock and other mocks - if I had written the implementation myself, I would probably have a setter method for <code>EntityManager</code>, allowing me to use my mock for the unit test. (As for actual database connectivity, I have a <code>JpaConfiguration</code> class, annotated with <code>@Configuration</code> and <code>@EnableJpaRepositories</code>, which programmatically defines beans for <code>DataSource</code>, <code>EntityManagerFactory</code>, <code>EntityManager</code> etc. - but repositories should be test-friendly and allow for overriding these things).</p></li>
  <li><p>Second, should I test for interactions? It is hard for me to figure out what methods of <code>EntityManager</code> and <code>Query</code> are supposed to be called (akin to that <code>verify(entityManager).createNamedQuery(anyString()).getResultList();</code>), since it isn't me who is writing the implementation.</p></li>
  <li><p>Third, am I supposed to unit-test the Spring-Data-generated methods in the first place? As I know, the third-party library code is not supposed to be unit-tested - only the code the developers write themselves is supposed to be unit-tested. But if that's true, it still brings the first question back to the scene: say, I have a couple of custom methods for my repository, for which I will be writing implementation, how do I inject my mocks of <code>EntityManager</code> and <code>Query</code> into the final, generated repository?</p></li>
  </ul>
  
  <p>Note: I will be test-driving my repositories using <em>both</em> the integration and the unit tests. For my integration tests I am using an HSQL in-memory database, and I am obviously not using a database for unit tests.</p>
  
  <p>And probably the fourth question, is it correct to test the correct object graph creation and object graph retrieval in the integration tests (say, I have a complex object graph defined with Hibernate)?</p>
  
  <p>Update: today I've continued experimenting with mock injection - I created a static inner class to allow for mock injection. </p>
  
  <pre><code>@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());
  }
  
  }
  </code></pre>
  
  <p>However, running this test gives me the following stacktrace:</p>
  
  <pre><code>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
  </code></pre>