在 Spring Boot 中使用模拟单元测试接口实现

Rob*_*eia 8 java spring unit-testing

我正在尝试为 Spring Boot 中的服务编写一个简单的单元测试。

该服务调用存储库上的方法,该方法返回用户的实例。我正在尝试模拟存储库,因为我只想测试服务。

因此,Repository的代码:

public interface UserRepository extends MongoRepository<User, String> {
  User findByEmail(String email);
}
Run Code Online (Sandbox Code Playgroud)

服务接口

public interface UserService {
  @Async
  CompletableFuture<User> findByEmail(String email) throws InterruptedException;
}
Run Code Online (Sandbox Code Playgroud)

服务实施

@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<User> findByEmail(String email) throws InterruptedException {
    User user = userRepository.findByEmail(email);
    return CompletableFuture.completedFuture(user);
  }
}
Run Code Online (Sandbox Code Playgroud)

单元测试

@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);
  }
}
Run Code Online (Sandbox Code Playgroud)

当我运行这个测试时,我得到了一个MockitoException

org.mockito.exceptions.base.MockitoException: 
Cannot instantiate @InjectMocks field named 'userService'.
...
Caused by: org.mockito.exceptions.base.MockitoException: the type 'UserService' is an interface.
Run Code Online (Sandbox Code Playgroud)

我没有使用接口,而是尝试使用真正的实现;像这样改变测试:

@InjectMocks
UserServiceImpl userService;
Run Code Online (Sandbox Code Playgroud)

现在,测试成功通过,但这似乎不正确(至少对我而言)。我喜欢测试 Spring Boot 正在使用的 UserService(假设在我的系统的新版本中,我实现了一个新的 UserServicePostgreSQLImpl - 现在我正在使用 MongoDB)。(编辑:请参阅问题中的底部编辑)

我改变了单元测试如下:

@Autowired
@InjectMocks
UserService userService;
Run Code Online (Sandbox Code Playgroud)

但现在我测试失败了:

Expected :model.User@383caf89
Actual   :null
Run Code Online (Sandbox Code Playgroud)

出于某种原因,当我使用 时@AutowiredUserRepository模拟不起作用。如果我将 更改为emailTest在我的数据库中使用真实的电子邮件,则测试通过。当我使用时@Autowired,测试使用的是真实版本UserRepository而不是.MockUserRepository

有什么帮助吗?

编辑:查看@msfoster 和@dunni 的答案,并更好地思考,我相信正确的方法是测试每个实现(在我的示例中,使用UserServiceImpl userService)。

Plo*_*log 3

为了让你的 UserServiceImpl 在使用 @InjectMocks 注释时自动装配,那么它需要注册为 Spring bean 本身。您可以通过使用 @Service 对 UserServiceImpl 类进行注释来最简单地完成此操作。

这将确保 Spring boot 配置中的组件扫描能够拾取它。(只要扫描包含您的服务类别所在的包!)