我正在尝试使用TDD技术实现我的Spring网站。
有一些TDD规则:
我创建了UsersService空类,它取决于crud UsersRepository。 现在,我正在尝试编写用于注册新用户的测试,但我不知道如何正确地执行此操作。
@Test
public void signUp_shouldCheckIfUserExistsBeforeSign() throws ServiceException {
// given
User user = new User();
user.setEmail(EMAIL);
when(usersRepository.save(user)).thenReturn(user);
when(usersRepository.exists(anyString())).thenReturn(Boolean.FALSE);
// when
usersService.signUp(user);
// then
thrown.expect(UserAlreadyExistsServiceException.class);
usersService.signUp(user);
}
此代码测试行为,但也强制我使用exists()方法而不是findByEmail()来实现我的服务。
这个测试应该怎么样?
答案 0 :(得分:2)
您的测试似乎反映了对行为和实施的一些混淆。当你第一次打电话给signUp()
时,你似乎期望状态发生变化,但是因为你使用的是模拟我认为不会发生这种情况,所以如果你这么做就不要再打signUp()
两次重新使用模拟(并且expect()应该在signUp()
之前,我相信)。如果你没有使用模拟,那么两次调用signUp()
将是一个有效的测试,没有实现依赖性,但你(明智地,恕我直言)使用模拟来避免缓慢的,依赖于数据库的测试,以便轻松模拟依赖关系,所以只需调用signUp()
一次,让模拟模拟状态。在测试服务行为时模拟存储接口是有意义的。
至于你的2个测试规则,你不能在没有实现概念的情况下使用模拟(我更喜欢将其视为“交互” - 特别是如果你模拟接口而不是具体的类)。您似乎采用模块化设计,因此我不担心模拟明显的交互。如果你稍后改变主意关于交互(无论你是否应该检索用户对象而不是布尔存在检查),你改变你的测试 - 没什么大不了的,恕我直言。进行单元测试应该会让你害怕改变你的代码。如果需要改变相互作用,模拟可以使测试更加脆弱。另一方面是你在编码之前更多地考虑这些交互,这很好,但不要卡住。
关于是否通过电子邮件检索用户或使用布尔exists()
调用检查其存在的困境听起来像是YAGNI的情况。如果你不知道你要检索的User
对象是什么,除了检查它是否为null之外,请使用布尔值。如果你以后改变主意,可能会有一些破坏的测试(轻松)修复,但你会更清楚地了解事情应该如何运作。
因此,如果您决定坚持exists()
:
@Test
public void signUp_shouldCheckIfUserExistsBeforeSign() throws ServiceException {
// given
User user = new User();
user.setEmail(EMAIL);
when(usersRepository.exists(anyString())).thenReturn(Boolean.FALSE);
thrown.expect(UserAlreadyExistsServiceException.class);
// when
usersService.signUp(user);
// then - no validation because of expected exception
}
BTW,(这是一个侧面问题,并且有许多不同的方法可以在StackOverflow上的其他地方进行测试中期待异常)能够进行expect()
调用会很好在“then”部分,但它必须在signUp()
之前。您也可以(如果您不想在“给定”部分中调用expect()),请使用expected
的{{1}}参数,而不是调用@Test
。显然,JUnit 5允许在一个期望调用中包装抛出调用,该调用返回抛出的异常,如果没有抛出则失败。
答案 1 :(得分:0)
测试行为很好,但是生产代码需要展示这种行为,这会在一定程度上影响实现。
将测试重点放在单一行为上:
@Test
public void signUpFailsIfUserEmailAlreadyExists() throws ServiceException {
// given
User user = new User();
user.setEmail(EMAIL);
when(usersRepository.emailExists(EMAIL)).thenReturn(Boolean.TRUE);
// when
usersService.signUp(user);
// then
thrown.expect(UserAlreadyExistsServiceException.class);
}