title: unit test a interface implementation with mock in Spring Boot
body: <p>I'm trying to write a simple unit test for a service in Spring Boot.</p>
  
  <p>The service calls a method on a repository which returns an instance of User.
  I'm trying to mock the repository, because I want to test only the service.</p>
  
  <p>So, the code for <strong>Repository</strong>:</p>
  
  <pre><code>public interface UserRepository extends MongoRepository&lt;User, String&gt; {
    User findByEmail(String email);
  }
  </code></pre>
  
  <p><strong>Service interface</strong>:</p>
  
  <pre><code>public interface UserService {
    @Async
    CompletableFuture&lt;User&gt; findByEmail(String email) throws InterruptedException;
  }
  </code></pre>
  
  <p><strong>Service implementation</strong>:</p>
  
  <pre><code>@Service
  public class UserServiceImpl implements UserService {
    private UserRepository userRepository;
  
    // dependency injection
    // don't need Autowire here
    // https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
    public UserServiceImpl(UserRepository userRepository) {
      this.userRepository = userRepository;
    }
  
    @Async
    public CompletableFuture&lt;User&gt; findByEmail(String email) throws InterruptedException {
      User user = userRepository.findByEmail(email);
      return CompletableFuture.completedFuture(user);
    }
  }
  </code></pre>
  
  <p><strong>Unit Test</strong>:</p>
  
  <pre><code>@RunWith(SpringRunner.class)
  @SpringBootTest
  public class UserServiceTest {
  
    @InjectMocks
    UserService userService;
  
    @Mock
    UserRepository mockUserRepository;
  
    @Before
    public void setUp() {
      MockitoAnnotations.initMock(this);
    }
  
    @Test
    public void mustReturnUser() throws InterruptedException {
      String emailTest = "foo@bar.com";
  
      User fakeUser = new User();
      fakeUser.setEmail(emailTest);
  
      when(mockUserRepository.findByEmail(emailTest)).thenReturn(fakeUser);
  
      User user = userService.findByEmail(emailTest).join();
      assertThat(user).isEqualTo(fakeUser);
  
      verify(mockUserRepository).findByEmail(emailTest);
    }
  }
  </code></pre>
  
  <p>When I run this test, I got a <code>MockitoException</code>:</p>
  
  <pre><code>org.mockito.exceptions.base.MockitoException: 
  Cannot instantiate @InjectMocks field named 'userService'.
  ...
  Caused by: org.mockito.exceptions.base.MockitoException: the type 'UserService' is an interface.
  </code></pre>
  
  <p>Instead of using the interface, I tried to use the real implementation; changing the test like this:</p>
  
  <pre><code>@InjectMocks
  UserServiceImpl userService;
  </code></pre>
  
  <p>Now, the test passes with success, but this don't appear be right (at least for me).
  I like to test the UserService that Spring Boot is using (suppose that in a new version of my system, I implement a new UserServicePostgreSQLImpl - now I'm using MongoDB).
  (edit: see the bottom edit in the question)</p>
  
  <p>I changed the Unit Test as follows:</p>
  
  <pre><code>@Autowired
  @InjectMocks
  UserService userService;
  </code></pre>
  
  <p>but now I got a test failure:</p>
  
  <pre><code>Expected :model.User@383caf89
  Actual   :null
  </code></pre>
  
  <p>For some reason, when I use <code>@Autowired</code>, the <code>UserRepository</code> mock doesn't work.
  If I change the <code>emailTest</code> to use a real email in my database,
  the test passes.
  When I use <code>@Autowired</code>,
  the test is using the real <code>UserRepository</code> and not a <code>Mock</code> version of <code>UserRepository</code>.</p>
  
  <p>Any help?</p>
  
  <p>Edit: looking at the answers from @msfoster and @dunni, and thinking better, I believe that the correct way is to test every implementation (in my example, use <code>UserServiceImpl userService</code>).</p>