2012-01-23 5 views
11

Şimdiye kadar SO cevaplarım benim için tamamen tatmin edici olmuştur. Ben Junit ve Mockito ile birim testi öğreniyorum ve Spring web uygulamasının bir parçası olan hizmet sınıfımı test etmek istiyorum. Pek çok dersi ve makaleyi okudum ve hizmet katmanım için uygun birim testleri yazmakta hala sorun yaşıyorum. Benim sorulara yanıt bilmek istiyorum, ama önce bazı kodu yapıştırın:Spring service unit testing mockito kullanarak

Servis sınıfı

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

Benim bitmemiş test sınıf

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

} 

Sorular :

  1. Hizmet sınıfımda olduğu gibi yöntemlerde yöntemlerim olduğunda birim sınamalarını yapmanın en iyi yolu nedir? Onları yukarıdaki gibi ayrı ayrı test edebilir miyim? [Kodumu temiz kodlara sahip olmak için birkaç yönteme ayırdım]
  2. Hizmet yöntemim için testRegisterNewAccount() iyi birim sınaması var mı? Test yeşil ancak bundan emin değilim.
  3. TestShouldSetRoleToAccount'ta hata alıyorum. Neyi yanlış yapıyorum?
  4. CheckIfUsernameExists nasıl test edilir?

Ben birkaç gün ödedim ve bir ilerleme :(


GÜNCELLEME yapmadığını çünkü Belki birisi bana bu konuda yardımcı olacaktır

Biten test sınıf

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

} 

cevap

5

Soru 1 - Bir çiftin var. seçeneklerin burada.

Seçenek 1 - Bu davranış için gerekenlere dayanarak, her bir kamu yönteminin her davranışı için ayrı ayrı testler yazın. Bu, her testi temiz ve ayrı tutar, fakat ikincil yöntemlerde (checkIfUsernameExists gibi) mantığın iki kez uygulanacağı anlamına gelir. Bir anlamda, bu gereksiz bir çoğaltmadır, ancak bu seçeneğin bir avantajı, uygulamayı değiştirir, ancak gerekli davranışı değiştirmezseniz, davranışa dayanan iyi testlere sahip olmanızdır.

Seçenek 2 - bir Mockito Spy kullanın. Bu bir alay gibi, gerçek bir nesneden yaratmanız dışında, ve varsayılan davranışı, tüm yöntemlerin her zamanki gibi çalışmasıdır. Daha sonra bunları çağıran yöntemleri sınamak için ikincil yöntemleri saptayabilir ve doğrulayabilirsiniz.

Soru 2 - Bu, "başarı" durumu registerNewAccount için iyi bir test gibi görünüyor. Lütfen hangi koşulların registerNewAccount başarısız ve yanlış döndürmesine neden olacağını düşünün; ve bu vakayı test edin. Soru 3 - Buna hiç iyi bakmamıştım; ancak hata ayıklayıcısını kullanarak çalışmayı deneyin ve nesnelerin beklediğinizden farklı olduğunu öğrenin. Eğer çalışamazsan, tekrar yayınla ve başka bir bakmam gerekecek.

Soru 4 - Olumsuz durumu sınamak için, gerekli özel durumu atmak üzere AccountDao alayını itin. Aksi halde, soru 1 için cevaplarıma bakın.

+0

Teşekkürler David. Soru 1'e cevap verin tamamen anlayın ve seçenek 1'i seçtim. Soru 3 Hakkında Bir problemi çözmeyi başardım. Mock Hesabını yeni operatör tarafından oluşturulan Hesap ile değiştirmek zorunda kaldım ve bu o oldu [sic!] :). Soru 4 de bana yardımcı oldu, ama başka sorunlarım var. İpuçlarını gördüğünüz gibi, tüm yöntemlerim için testler yazmayı başardım. TestShouldNotSetRoleToAccount ve testShouldNotLoadUserByUsername haricinde çalışırlar. Her ikisi de "beklenen = ..." olduğunda başarısız olur. Onsuz tamam. Ayrıca ilk olarak aynı zamanda hata yapar ve test Hatadır. Bana yardım eder misin? –

+0

Üzgünüz, size geri dönmem biraz zaman aldı. Bu testler beklenen istisna setiyle başarısız olursa, istisna aslında atılmadığı anlamına gelir. 'RolDao' ve 'accountDao' un aslında sahtekarlara ayarlandığından emin misiniz? Bunu hata ayıklayıcı ile kontrol edebilirsiniz. Ayrıca, "anyString()" işlevini doğru bir şekilde kullanmıyorsunuz - bu, aslında yönteminizi çalıştırmamak için, saplama ve doğrulama amaçlıdır; Bunun senin sorununun sebebi olup olmadığını bilmiyorum. Yöntemlerinizi gerçekten çalıştırdığınız satırlarda, "anyString()" yerine geçirmek istediğiniz gerçek değeri koyun. –

+0

Eh, ben aptalım. Bu konuda konuştuğum bu hata sadece logger'dan konsol bilgisi idi. Özel durum yakalandığında roleDao'de logger.error (..): P var. Test koduma iyice baktım ve şimdi her şey yolunda. testShouldNotSetRoleToAccount'ta "beklenen" ve testCheckIfUsernameExistsIsFalse gerekli değildir. Daha sonra test sınıfımı güncelleyeceğim ve bu tartışmayı kapatabiliriz. Bir kez daha teşekkürler David, ipuçlarınız çok yardımcı oldu :) –