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 :
- ¿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]
- ¿Test testtest new account() es una buena prueba de unidad para mi método de servicio? La prueba es verde, pero no estoy seguro.
- Recibo un error en mi testShouldSetRoleToAccount. ¿Qué estoy haciendo mal?
- 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());
}
}
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? –
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()'. –
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 :) –