假设我有以下服务对象
public class UserService {
@Autowired
private UserDao dao;
public void addUser(String username, String password) {
if (username.length() < 8 ) {
username = username + "random" ; // add some random string
}
User user = new User(username, password);
dao.save(user);
}
}
我想测试方法的行为&#34; addUser&#34;当用户名长度小于8且用户名大于8个字符时。如何在单元测试UserService.addUser(...)中进行验证并验证它?我知道使用assert(),但值&#34;密码&#34;在addUser(...)方法之外不可用。
我使用JUnit和Mockito。
答案 0 :(得分:7)
我提出了一个解决方案,几个月后再次重新访问问题。
我们的想法是观察传递给UserDao的对象用户。我们可以通过这样做检查用户名的值,因此单元测试代码:
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
@Mock
private UserDao dao;
@InjectMock
private UserService service;
@Test
public void testAddingUserWithLessThan8CharUsername () {
final String username = "some";
final String password = "user";
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
Object[] args = invocationOnMock.getArguments();
User toBeSaved = (User) args[0];
Assert.assertEquals(username + "random", toBeSaved.getPassword());
return null;
}
}).when(userDao).save(Matchers.any(User.class));
service.addUser(username, password);
}
}
Guillaume实际上有最接近的答案,但他回答使用jMock。但是,他给了我关于如何实现这个目标的想法,所以我认为他也应该得到一些荣誉。
答案 1 :(得分:1)
您正在测试副作用,但幸运的是,您需要的所有内容都会传递给dao.save()。首先,创建一个UserDao(有或没有Mockito),然后你可以使用ReflectionTestUtils在UserService中设置dao,然后你可以测试传递给dao.save()的值。
类似的东西:
private class TestUserDao extends UserDao {
private User savedUser;
public void save(User user) {
this.savedUser = user;
}
}
@Test public void testMethod() {
UserService userService = new UserService();
TestUserDao userDao = new TestUserDao();
ReflectionTestUtils.setField(userService, "dao", userDao);
userService.addUser("foo", "bar");
assertEquals("foo", userDao.savedUser.username.substring(0, 3));
assertEquals("bar", userDao.savedUser.password);
}
或者如果你愿意的话,你可以使用Mockito模仿Dao。
答案 2 :(得分:1)
使用模拟框架。以下示例使用JMock2
,但与EasyMock
,Mockito
等类似。
此外,您需要将用户名生成提取为类似UsernameGenmerator
的内容,以便能够模拟它。您需要为用户名生成器进行另一项特定测试。
private final Mockery mockery = new Mockery();
private final UserDao mockDao = mockery.mock(UserDao.class);
private final UsernameGenerator mockUserNameGenerator = mockery.mock(UsernameGenerator.class);
@Test
public void addUserUsesDaoToSaveUser() {
final String username = "something";
final String generatedUsername = "siomething else";
final String password = "a password";
mockery.checking(new Expectations() {{
oneOf(mockUsernameGenerator).generateUsername(username);
will(returnValue(generatedUsername));
oneOf(mockDao).save(new User(generatedUsername, password)); // assumes your User class has a "natueral" equals/hashcode
}});
UserService userService = new UserService();
userService.addUser(username, password);
}
对于UsernameGenerator
,您需要测试返回用户名的长度:
@Test
public void leavesUsernameUnchangedIfMoreThanEightChars() {
final String username = "123456789";
final UsernameGenerator usernameGenerator = new UsernameGenerator();
assertEquals(username, userGenerator.generateUsername(username));
}
@Test
public void addsCharactersToUsernameIfLessThanEightChars() {
final String username = "1234567";
final UsernameGenerator usernameGenerator = new UsernameGenerator();
assertEquals(8, userGenerator.generateUsername(username).length());
}
当然,根据您的“随机”方法,您可能也想测试其特定行为。除此之外,上述内容为您的代码提供了很好的覆盖范围。
答案 3 :(得分:0)
这完全取决于你的DAO的保存方法是如何实现的。
如果您实际存储到硬编码存储库,那么您可能需要查询存储库本身以获取您所参与的值。
如果你有一个被调用的底层接口,那么你应该能够设置一个回调方法并检索正在保存的实际值。
我从未使用过Mockito所以我无法给出确切的代码,本文应该解决这个问题:
Using Mockito, how do I intercept a callback object on a void method?
答案 4 :(得分:0)
考虑将用户名生成逻辑从UserService中提取为依赖项。
interface UserNameGenerator {
Strign generate();
}
与UserNameGenerator
相同的电话UserDao
。并将代码更改为:
public class UserService {
@Autowired
private UserDao dao;
@Autowired
private UserNameGenerator nameGenerator;
public void addUser(String username, String password) {
if (username.length() < 8 ) {
username = nameGenerator.generate();
}
User user = new User(username, password);
dao.save(user);
}
}
接下来创建UserNameGenerator
的默认实现,并在那里移动名称生成逻辑。
现在,您可以通过模拟UserNameGenerator
和UserDao
轻松检查行为。
在用户名长度小于8
时检查用例String username = "123";
String password = "pass";
String generatedName = "random";
// stub generator
when(nameGenerator.generate()).thenReture(generatedName);
// call the method
userService.addUser(username, password);
// verify that generator was called
verify(nameGenerator).generate();
verify(userDao).save(new User(generatedName, password));
在用户名长度大于8
时检查用例String username = "123456789";
String password = "pass";
String generatedName = "random";
// call the method
userService.addUser(username, password);
// verify that generator was never called
verify(nameGenerator, never()).generate();
verify(userDao).save(new User(username, password));
答案 5 :(得分:-1)
最简单的方法是提取具有用户名更正逻辑的部分
if (username.length() < 8 ) {
username = username + "random" ; // add some random string
}
进入方法并测试该方法的返回值。
public string GetValidUsername(string userName){
if (username.length() < 8 ) {
return username + "random" ; // add some random string
}
return username;
}
通过这种方式,您可以传递不同类型的用户名并测试代码的行为。