2012-01-23 27 views
11

これまでのところ、SOからの回答は私の問題を完全に満足させていました。私はJunitとMockitoの単体テストを学んでおり、Spring Webアプリケーションの一部であるサービスクラスをテストしたいと思います。私は多くのチュートリアルや記事を読んでいますが、私はまだ自分のサービス層に適切な単体テストを書く上で問題があります。私は私の質問のための答えを知りたいのですが、最初、私はいくつかのコードを貼り付けます。mockitoを使用したSpringサービスユニットのテスト

Serviceクラス

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

私の未完成のテストクラス

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

} 

質問を

  1. 私のサービスクラスのようなメソッドでメソッドがある場合に単体テストを行う最良の方法は何ですか?上記のように個別にテストすることはできますか? [私のコードをいくつかのメソッドに分割して、よりクリーンなコードにする]
  2. 私のサービスメソッドのための良いユニットテストですか?テストは緑ですが、私はそれについてはわかりません。
  3. 私はtestShouldSetRoleToAccountに失敗しています。私は間違って何をしていますか?
  4. checkIfUsernameExistsをテストするにはどうすればよいですか?

私は数日を過ごし、私は進歩をしなかったので、多分誰かがこれで私を助ける:(


UPDATE

完成テストクラス

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

} 

答えて

5

質問1 - あなたはカップルを持っていますここのオプションの

オプション1 - その動作に必要なものに基づいて、各パブリックメソッドの動作ごとに個別のテストを記述します。これにより、各テストはきれいに分離されたままになりますが、セカンダリメソッド(checkIfUsernameExistsなど)のロジックが2回実行されることを意味します。つまり、これは不要な重複ですが、このオプションの利点の1つは、実装を変更して必要な動作を変更しないと、その動作に基づいて良好なテストが行​​われることです。

オプション2 - Mockito Spyを使用します。これは実際のオブジェクトから作成することを除いて、ちょっと似ていますが、デフォルトの動作ではすべてのメソッドがいつものように実行されます。その後、それらを呼び出すメソッドをテストするために、スタブアウトしてセカンダリメソッドを検証することができます。

質問2 - これはregisterNewAccountの「成功」の場合の良いテストのようです。どのような状況で失敗してfalseを返すか考えてください。このケースをテストします。

質問3 - 私はこれをよく見ていませんでした。デバッガで実行してみて、オブジェクトが期待どおりでない点を見つけてください。もしあなたがそれを解決できないのであれば、私はもう一度投稿してください。

質問4-ネガティブケースをテストするには、AccountDaoのモックをスタブして必要な例外をスローします。それ以外の場合は、質問1の回答を参照してください。

+0

Davidありがとうございます。質問1に返信私は完全に理解しており、私はオプション1を選択しました。質問3について私は問題を解決することができました。私は、新しいオペレータによって作成されたアカウントと模擬アカウントを置き換えなければなりませんでした。それは[sic!] :)でした。質問4も私を助けましたが、私には他の問題があります。あなたのヒントのおかげで、私はすべての方法のテストを書くことができました。彼らは、testShouldNotSetRoleToAccountとtestShouldNotLoadUserByUsernameを除いてOKです。 "expected = ..."があると両方が失敗します。それなしでOK。さらに、最初にenエラーを作成し、テストは Errorです。私たちを手伝ってくれますか? –

+0

申し訳ありませんが、私はあなたに戻って取得するためにしばらく時間がかかりました。これらのテストが期待される例外セットで失敗した場合、例外が実際にスローされないことを意味します。 'roleDao'と' accountDao'が実際にモックに設定されていますか?これはデバッガで確認できます。また、あなたは 'anyString()'を正しく使用していません - これはスタブと検証のためのもので、実際にメソッドを実行しているわけではありません。これがあなたの問題の原因かどうかはわかりません。実際にあなたのメソッドを実行する行では、 'anyString()'の代わりに渡す実際の値を入れてください。 –

+0

ええ、私はばかです。私が話していたこのエラーは、ロガーのコンソール情報でした。例外が捕捉されたときのroleDaoにはlogger.error(..):Pがあります。私は徹底的にテストコードを見て、今はすべてOKです。 testShouldNotSetRoleToAccountの "expected"とtestCheckIfUsernameExistsIsFalseは必要ありません。後でテストクラスを更新し、このディスカッションを閉じることができます。もう一度Davidに感謝します。あなたのヒントはとても役に立ちました:) –

関連する問題