2012-01-23 17 views
11

Hasta ahora las respuestas de SO han sido completamente satisfactorias para mis problemas. Estoy aprendiendo pruebas unitarias con Junit y Mockito y quiero probar mi clase de servicio que es parte de mi aplicación web Spring. He leído muchos tutoriales y artículos y todavía tengo problemas para escribir las pruebas de unidad adecuadas para mi capa de servicio. Me gustaría saber las respuestas para mis preguntas, pero primero pegar algo de código:Prueba de unidad de servicio de primavera usando mockito

La clase de servicio

public class AccountServiceImpl implements AccountService { 

@Autowired 
AccountDao accountDao, RoleDao roleDao, PasswordEncoder passwordEncoder, SaltSource saltSource; 

@PersistenceContext 
EntityManager entityManager; 

public Boolean registerNewAccount(Account newAccount) { 
    entityManager.persist(newAccount); 
    newAccount.setPassword(passwordEncoder.encodePassword(newAccount.getPassword(), saltSource.getSalt(newAccount))); 
    setRoleToAccount("ROLE_REGISTERED", newAccount); 

    return checkIfUsernameExists(newAccount.getUsername());  
} 

public void setRoleToAccount(String roleName, Account account) { 
    List<Role> roles = new ArrayList<Role>(); 
    try { 
     roles.add(roleDao.findRole(roleName)); 
    } catch(RoleNotFoundException rnf) { 
     logger.error(rnf.getMessage()); 
    } 
    account.setRoles(roles); 
} 

public Boolean checkIfUsernameExists(String username) { 
    try { 
     loadUserByUsername(username); 
    } catch(UsernameNotFoundException unf) { 
     return false; 
    } 
    return true; 
} 

public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 
    try { 
     Account loadedAccount = accountDao.findUsername(username); 
     return loadedAccount; 
    } catch (UserNotFoundException e) { 
     throw new UsernameNotFoundException("User: " + username + "not found!"); 
    } 
} 
} 

Mi clase de prueba sin terminar

@RunWith(MockitoJUnitRunner.class) 
public class AccountServiceImplTest { 

private AccountServiceImpl accountServiceImpl; 
@Mock private Account newAccount; 
@Mock private PasswordEncoder passwordEncoder; 
@Mock private SaltSource saltSource; 
@Mock private EntityManager entityManager; 
@Mock private AccountDao accountDao; 
@Mock private RoleDao roleDao; 

@Before 
public void init() { 
    MockitoAnnotations.initMocks(this); 
    accountServiceImpl = new AccountServiceImpl(); 
    ReflectionTestUtils.setField(accountServiceImpl, "entityManager", entityManager); 
    ReflectionTestUtils.setField(accountServiceImpl, "passwordEncoder", passwordEncoder); 
    ReflectionTestUtils.setField(accountServiceImpl, "saltSource", saltSource); 
    ReflectionTestUtils.setField(accountServiceImpl, "accountDao", accountDao); 
    ReflectionTestUtils.setField(accountServiceImpl, "roleDao", roleDao); 
} 

@Test 
public void testRegisterNewAccount() { 
    Boolean isAccountCreatedSuccessfully = accountServiceImpl.registerNewAccount(newAccount); 

    verify(entityManager).persist(newAccount); 
    verify(newAccount).setPassword(passwordEncoder.encodePassword(newAccount.getPassword(), saltSource.getSalt(newAccount))); 
    assertTrue(isAccountCreatedSuccessfully); 
} 

@Test 
public void testShouldSetRoleToAccount() throws RoleNotFoundException{ 
    Role role = new Role(); //Maybe I can use mock here? 
    role.setName("ROLE_REGISTERED"); 
    when(roleDao.findRole("ROLE_REGISTERED")).thenReturn(role); 
    accountServiceImpl.setRoleToAccount("ROLE_REGISTERED", newAccount); 
    assertTrue(newAccount.getRoles().contains(role)); 
} 

} 

Preguntas :

  1. ¿Cuál es la mejor manera de hacer pruebas unitarias donde tengo métodos en métodos como en mi clase de servicio? ¿Puedo probarlos por separado como arriba? [Dividí mi código en algunos métodos para tener un código más limpio]
  2. ¿Test testtest new account() es una buena prueba de unidad para mi método de servicio? La prueba es verde, pero no estoy seguro.
  3. Recibo un error en mi testShouldSetRoleToAccount. ¿Qué estoy haciendo mal?
  4. Cómo probar checkIfUsernameExists?

Tal vez alguien me va a ayudar con esto porque pasé un par de días y no hice un progreso :(


ACTUALIZACIÓN

clase de prueba terminado

@RunWith(MockitoJUnitRunner.class) 
public class AccountServiceImplTest extends BaseTest { 

private AccountServiceImpl accountServiceImpl; 
private Role role; 
private Account account; 
@Mock private Account newAccount; 
@Mock private PasswordEncoder passwordEncoder; 
@Mock private SaltSource saltSource; 
@Mock private EntityManager entityManager; 
@Mock private AccountDao accountDao; 
@Mock private RoleDao roleDao; 

@Before 
public void init() { 
    accountServiceImpl = new AccountServiceImpl(); 
    role = new Role(); 
    account = new Account(); 
    ReflectionTestUtils.setField(accountServiceImpl, "entityManager", entityManager); 
    ReflectionTestUtils.setField(accountServiceImpl, "passwordEncoder", passwordEncoder); 
    ReflectionTestUtils.setField(accountServiceImpl, "saltSource", saltSource); 
    ReflectionTestUtils.setField(accountServiceImpl, "accountDao", accountDao); 
    ReflectionTestUtils.setField(accountServiceImpl, "roleDao", roleDao); 
} 

@Test 
public void testShouldRegisterNewAccount() { 
    Boolean isAccountCreatedSuccessfully = accountServiceImpl.registerNewAccount(newAccount); 

    verify(entityManager).persist(newAccount); 
    verify(newAccount).setPassword(passwordEncoder.encodePassword(newAccount.getPassword(), saltSource.getSalt(newAccount))); 
    assertTrue(isAccountCreatedSuccessfully); 
} 

@Test(expected = IllegalArgumentException.class) 
public void testShouldNotRegisterNewAccount() { 
    doThrow(new IllegalArgumentException()).when(entityManager).persist(account); 
    accountServiceImpl.registerNewAccount(account); 
} 

@Test 
public void testShouldSetRoleToAccount() throws RoleNotFoundException { 
    when(roleDao.findRole(anyString())).thenReturn(role); 
    accountServiceImpl.setRoleToAccount("ROLE_REGISTERED", account); 
    assertTrue(account.getRoles().contains(role)); 
} 

@Test 
public void testShouldNotSetRoleToAccount() throws RoleNotFoundException { 
    when(roleDao.findRole(anyString())).thenThrow(new RoleNotFoundException()); 
    accountServiceImpl.setRoleToAccount("ROLE_RANDOM", account); 
    assertFalse(account.getRoles().contains(role)); 
} 

@Test 
public void testCheckIfUsernameExistsIsTrue() throws UserNotFoundException { 
    when(accountDao.findUsername(anyString())).thenReturn(account); 
    Boolean userExists = accountServiceImpl.checkIfUsernameExists(anyString()); 
    assertTrue(userExists); 
} 

@Test 
public void testCheckIfUsernameExistsIsFalse() throws UserNotFoundException { 
    when(accountDao.findUsername(anyString())).thenThrow(new UserNotFoundException()); 
    Boolean userExists = accountServiceImpl.checkIfUsernameExists(anyString()); 
    assertFalse(userExists); 
} 

@Test 
public void testShouldLoadUserByUsername() throws UserNotFoundException { 
    when(accountDao.findUsername(anyString())).thenReturn(account); 
    Account foundAccount = (Account) accountServiceImpl.loadUserByUsername(anyString()); 
    assertEquals(account, foundAccount); 
} 

@Test(expected = UsernameNotFoundException.class) 
public void testShouldNotLoadUserByUsername() throws UserNotFoundException { 
    when(accountDao.findUsername(anyString())).thenThrow(new UsernameNotFoundException(null)); 
    accountServiceImpl.loadUserByUsername(anyString()); 
} 

} 

Respuesta

5

Pregunta 1 - Tienes un par de opciones aquí.

Opción 1: escriba pruebas separadas para cada comportamiento de cada método público, en función de lo que se requiere para ese comportamiento. Esto mantiene cada prueba limpia y separada, pero significa que la lógica en los métodos secundarios (como checkIfUsernameExists) se ejercerá dos veces. En cierto sentido, esto es una duplicación innecesaria, pero una de las ventajas de esta opción es que si cambia la implementación, pero no el comportamiento requerido, seguirá teniendo buenas pruebas basadas en el comportamiento.

Opción 2: utiliza un Mockito Spy. Esto es un poco como un simulacro, excepto que lo creas desde un objeto real, y el comportamiento predeterminado es que todos los métodos se ejecuten como de costumbre. A continuación, puede anular y verificar los métodos secundarios para probar los métodos que los llaman.

Pregunta 2 - Esto parece una buena prueba para el caso de "éxito" de registerNewAccount. Por favor, piense en qué circunstancias causarían que registerNewAccount fallara y devolviera el valor falso; y prueba este caso.

Pregunta 3: No he tenido un buen vistazo a esto; pero intente ejecutar con el depurador y descubra en qué punto sus objetos difieren de lo que espera. Si no puede resolverlo, publíquelo nuevamente y lo miraré de nuevo.

Pregunta 4 - Para probar el caso negativo, coloque su simulacro del AccountDao para lanzar la excepción requerida. De lo contrario, consulte mis respuestas a la pregunta 1.

+0

Gracias David. Responder a la pregunta 1 lo entiendo completamente y elegí la opción 1. Sobre la pregunta 3 logré resolver un problema. Tuve que reemplazar la cuenta simulada con la cuenta creada por el nuevo operador y fue [sic!] :). La pregunta 4 también me ayudó, pero tengo otros problemas. Como puede ver gracias a sus sugerencias, pude escribir pruebas para todos mis métodos. Funcionan bien, excepto para testShouldNotSetRoleToAccount y testShouldNotLoadUserByUsername. Ambos fallan cuando hay "esperado = ...". Sin eso está bien. Además, primero también hace un error y la prueba es Error. ¿Usted me podría ayudar? –

+0

Lo siento, me ha tomado un tiempo volver a verte. Si esas pruebas fallan con el conjunto de excepciones esperado, eso significa que la excepción no se está lanzando realmente. ¿Estás seguro de que 'roleDao' y' accountDao' se han establecido realmente en los simulacros? Puede verificar esto con el depurador. Además, no está utilizando 'anyString()' correctamente; esto es para copiar y verificar, no para ejecutar realmente su método; No sé si esta es la causa de tu problema. En las líneas donde realmente ejecuta sus métodos, coloque el valor real que desea pasar, en lugar de 'anyString()'. –

+0

Eh lol, soy estúpido. Este error del que estaba hablando era solo información de la consola del registrador. En roleDao cuando se captura una excepción, hay logger.error (..): P. Miré mi código de prueba a fondo y ahora todo está bien. "esperado" en testShouldNotSetRoleToAccount y testCheckIfUsernameExistsIsFalse no es necesario. Más tarde actualizaré mi clase de prueba y podemos cerrar esta discusión. Gracias una vez más, David, tus consejos fueron muy útiles :) –