2012-01-11 12 views
8

Supponiamo che io ho il seguente oggetto di servizioTest del comportamento del metodo di vuoto

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

voglio testare il comportamento del metodo "addUser" quando la lunghezza nome utente è meno 8 e quando il nome utente è più di 8 caratteri . Come approccio in unit test UserService.addUser (...) e verificarlo? Sono a conoscenza dell'uso di assert(), ma il valore "password" non è disponibile al di fuori del metodo addUser (...).

Uso JUnit e Mockito.

risposta

6

Mi è venuta una soluzione, dopo qualche ri-visita il problema di nuovo dopo alcuni mesi.

L'idea è di osservare l'utente dell'oggetto che viene passato a UserDao. Siamo in grado di controllare il valore del nome utente in questo modo, da qui il codice di prova di unità:

@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 effettivamente avuto la risposta più vicina, ma lui rispose con jMock. Tuttavia, mi ha dato l'idea su come realizzare questo, quindi penso che meriti anche qualche credito.

-1

Il modo più semplice è quello di estrarre la parte in cui si ha la logica di correzione nome utente

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

in un metodo e verificare il valore di ritorno di questo metodo.

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

con questo si può passare diversi tipi di nome utente e testare il comportamento del codice.

+0

Ho pensato a questo, ma il caso reale è già composto da diversi metodi. Quello che ho scritto qui è la versione più semplificata del caso reale. –

+0

Nonostante il neg. voto Continuo a pensare che renderà le cose più semplici da testare la logica di validazione e modifica del nome utente in isolamento (separatamente dal test di ciò che viene passato nel metodo di salvataggio). Ancor di più, se quella logica ha altre complessità. E penso anche che non sarà necessario usare alcun tipo di derisione per testare quella logica. – derdo

1

Si stanno testando gli effetti collaterali, ma per fortuna tutto ciò che serve è passato a dao.save(). Per prima cosa, crea un UserDao (con o senza Mockito), quindi puoi usare ReflectionTestUtils per impostare il dao nel UserService, quindi puoi testare i valori passati a dao.save().

Qualcosa di simile:

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

Oppure si può utente Mockito per deridere il Dao, se si desidera.

+0

Questo non è un Mock, questo è un Stub. Controlla che: http://martinfowler.com/articles/mocksArentStubs.html – Guillaume

+0

Sì, lo so, ma stavo usando la stessa terminologia dell'OP. Ho cambiato il testo. –

+0

Ho provato questo approccio, usando Mockito per prendere in giro l'oggetto UserDao. Sfortunatamente, ReflectionTestUtils restituisce sempre un oggetto null user null. Hai fatto qualcosa di sbagliato? –

0

Dipende tutto da come viene implementato il metodo di salvataggio del DAO.

Se in realtà si sta memorizzando in un repository hard-coded, allora si avrà probabilmente bisogno di interrogare il repository stesso per i valori che potrebbero interessare anche i.

Se si dispone di un'interfaccia di fondo che si chiama, allora si dovrebbe essere in grado di impostare un metodo di callback e recuperare il valore effettivo che viene salvato.

non ho mai usato Mockito quindi non si potrebbe dare il codice esatto che fa questo articolo dovrebbe affrontare questo:

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

0

consideri l'estrazione logica di generazione nome utente come la dipendenza da UserService.

interface UserNameGenerator { 
    Strign generate(); 
} 

Wire UserNameGenerator stessa UserDao. E modificare il codice a:

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

Avanti creare l'implementazione predefinita di UserNameGenerator e spostare nome generare logica lì.

Ora è possibile controllare facilmente il comportamento prendendo in giro UserNameGenerator e UserDao.

Per controllare caso di utilizzo quando username is lunghezza è inferiore a 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)); 

Per controllare caso di utilizzo quando username is lunghezza è maggiore 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

Usare un quadro di scherno . L'esempio seguente utilizza JMock2, ma sarebbe simile a EasyMock, Mockito, ecc. Inoltre, è necessario estrarre la generazione del nome utente su qualcosa come UsernameGenmerator per riuscire a prenderlo in giro. Hai bisogno di un altro test specifico per il generatore di nome utente.

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

E per UsernameGenerator è necessario prova sulla lunghezza del nome utente restituita:

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

Naturalmente, a seconda del metodo "random", si consiglia di testare il suo comportamento specifico troppo. Oltre a ciò, quanto sopra fornisce una buona copertura per il tuo codice.

Problemi correlati