Тестирование поведения пустого метода - PullRequest
8 голосов
/ 11 января 2012

Предположим, у меня есть следующий сервисный объект

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);
    }
}

Я хочу проверить поведение метода "addUser", когда длина имени пользователя меньше 8, а когда имя пользователя больше 8 символов. Как подойти в модульном тестировании UserService.addUser (...) и проверить это? Я знаю, как использовать assert (), но значение «пароль» не доступно за пределами метода addUser (...).

Я использую JUnit и Mockito.

Ответы [ 6 ]

7 голосов
/ 02 июля 2012

Я нашел решение, после некоторого повторного посещения проблемы через несколько месяцев.

Идея состоит в том, чтобы наблюдать за пользователем объекта, который передается в 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);
    }
}

У Гийома был самый близкий ответ, но он ответил, используя jMock. Тем не менее, он дал мне идею о том, как этого добиться, поэтому я думаю, что он тоже заслуживает похвалы.

1 голос
/ 11 января 2012

Используйте насмешливые рамки. В приведенном ниже примере используется 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());
}

Конечно, в зависимости от вашего «случайного» метода, вы также можете проверить его конкретное поведение. Кроме того, вышесказанное обеспечивает достаточное покрытие для вашего кода.

1 голос
/ 11 января 2012

Вы тестируете побочные эффекты, но, к счастью, все, что вам нужно, передается в dao.save ().Сначала создайте UserDao (с Mockito или без него), затем вы можете использовать ReflectionTestUtils для установки dao в UserService, затем вы можете проверить значения, которые передаются 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, чтобы издеваться над Дао, если хотите.

0 голосов
/ 11 января 2012

Рассмотрите возможность извлечения логики генерации имени пользователя как зависимости от 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));
0 голосов
/ 11 января 2012

Все будет зависеть от того, как реализован метод сохранения вашего DAO.

Если вы храните данные в жестко запрограммированном хранилище, вам, вероятно, потребуется запросить в самом хранилище значения, в которых вы заинтересованы.

Если у вас есть базовый интерфейс, которыйПосле этого вы сможете настроить метод обратного вызова и получить фактическое значение, которое сохраняется.

Я никогда не использовал Mockito, поэтому я не могу дать вам точный код, который в этой статье должен учитывать:

Как с помощью Mockito перехватить объект обратного вызова в методе void?

0 голосов
/ 11 января 2012

Самый простой способ - извлечь часть, в которой есть логика исправления имени пользователя

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;
}

с этим вы можете передать различные типы имени пользователя и протестировать поведение вашего кода.

...