2012-01-11 20 views
8

Załóżmy, że mam następujący obiekt usługaTestowanie zachowanie metody void

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

Chcę przetestować zachowanie metody „adduser”, gdy długość nazwy użytkownika jest mniejsza 8 i gdy nazwa użytkownika jest więcej niż 8 char . W jaki sposób podejście w jednostce testowej UserService.addUser (...) i zweryfikować? Zdaję sobie sprawę z używania assert(), ale wartość "password" nie jest dostępna poza metodą addUser (...).

Używam JUnit i Mockito.

Odpowiedz

6

Natrafiłem na rozwiązanie, po kilku ponownych wizytach problemu po kilku miesiącach.

Chodzi o to, aby obserwować użytkownika obiektu, który jest przekazywany do UserDao. Możemy sprawdzać wartość nazwa_użytkownika w ten sposób, stąd kod testów jednostkowych:

@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 rzeczywiście miał najbliższy odpowiedź, ale on odpowiedział użyciem JMock. Jednak dał mi pomysł, jak to osiągnąć, więc myślę, że on też zasługuje na trochę.

-1

Najprostszym sposobem jest wyodrębnienie części, w której masz nazwy użytkownika korekcji logikę

if (username.length() < 8) { 
    username = username + "random" ; // add some random string 
} 

do metody i testować wartość zwracaną tej metody.

public string GetValidUsername(string userName){ 
    if (username.length() < 8) { 
     return username + "random" ; // add some random string 
    } 
    return username; 
} 

dzięki temu można przekazywać różne typy nazwy użytkownika i testować zachowanie kodu.

+0

Myślałem o tym, ale faktyczny przypadek składa się już z kilku metod. To, co tu napisałem, to bardziej uproszczona wersja faktycznego przypadku. –

+0

Pomimo neg. głosowanie Nadal uważam, że ułatwi to testowanie logiki walidacji i modyfikacji nazwy użytkownika w oderwaniu (niezależnie od testowania, co zostanie przekazane do metody zapisu). Tym bardziej, jeśli ta logika ma inne zawiłości. Sądzę też, że nie będziesz musiał używać żadnego szyderstwa do testowania tej logiki. – derdo

1

Testujesz efekty uboczne, ale na szczęście wszystko, czego potrzebujesz, jest przekazywane do funkcji dao.save(). Najpierw utwórz UserDao (z lub bez Mockito), następnie możesz użyć ReflectionTestUtils, aby ustawić dao w UserService, następnie możesz przetestować wartości, które są przekazywane do dao.save().

Coś jak:

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

lub można użytkownik Mockito drwić z DAO, jeśli chcesz.

+0

To nie jest makieta, to jest skrót. Sprawdź to: http://martinfowler.com/articles/mocksArentStubs.html – Guillaume

+0

Tak, wiem, ale używałem tej samej terminologii co OP. Zmieniłem tekst. –

+0

Próbowałem tego podejścia, używając Mockito do kpiny z obiektu UserDao. Niestety, ReflectionTestUtils zawsze zwraca obiekt zerowy userDao. Zrobiłeś coś złego? –

0

Wszystko to zależy od sposobu implementacji metody zapisu DAO.

Jeśli faktycznie zapisywania na dysk kodowany repozytorium, a następnie będzie prawdopodobnie trzeba zapytać samego repozytorium dla wartości, które są intereseted w.

Jeśli masz podstawowy interfejs, który jest nazywany, to powinien mieć możliwość ustawienia metody wywołania zwrotnego i pobrania faktycznej wartości, która jest zapisywana.

nigdy nie używane Mockito więc nie mogę dać Ci kod, który robi dokładnie to artykuł powinien zająć to:

Using Mockito, how do I intercept a callback object on a void method?

0

Rozważmy wydobywania logikę generowania nazw użytkownika, jak uzależnienie od UserService.

interface UserNameGenerator { 
    Strign generate(); 
} 

drutu UserNameGenerator samo jak UserDao. I zmienić kod do:

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

Następny tworzyć realizację domyślnej UserNameGenerator i przenieść tam nazwa generowania logiki.

Teraz możesz łatwo sprawdzić zachowanie, kpiąc z UserNameGenerator i UserDao.

Aby sprawdzić przypadek użycia podczas nazwa jest długość jest mniejsza niż 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)); 

Aby sprawdzić przypadek użycia podczas nazwa jest długość jest większa niż 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)); 
1

Zastosowanie ramy szyderczy . Poniższy przykład używa JMock2, ale byłby podobny z EasyMock, Mockito, itp. Musisz także wyodrębnić generację nazwy użytkownika do czegoś podobnego do UsernameGenmerator, aby móc ją sfałszować. Potrzebujesz innego specyficznego testu dla generatora username.

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

I UsernameGenerator trzeba test na długość zwróconego Nazwa użytkownika:

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

Oczywiście, w zależności od metody „random”, może chcesz przetestować swoje specyficzne zachowanie też. Poza tym powyższe zapewniają złą ochronę twojego kodu.

Powiązane problemy