基本的にアプリケーションのランチャーであるコンポーネントのセットアップがあります。次のように構成されます。
@Component
public class MyLauncher {
@Autowired
MyService myService;
//other methods
}
MyServiceには@Service
Springアノテーションが付けられており、問題なくランチャークラスに自動接続されています。
MyLauncherのjUnitテストケースをいくつか作成したいので、次のようなクラスを開始しました。
public class MyLauncherTest
private MyLauncher myLauncher = new MyLauncher();
@Test
public void someTest() {
}
}
MyServiceのMockオブジェクトを作成し、テストクラスのmyLauncherに挿入できますか?現在、Springが自動配線を処理しているため、myLauncherにゲッターまたはセッターはありません。可能であれば、ゲッターとセッターを追加する必要はありません。 @Before
initメソッドを使用して、自動配線された変数にモックオブジェクトを挿入するようにテストケースに指示できますか?
これについて完全に間違っている場合は、お気軽にそれを言ってください。私はまだこれが初めてです。私の主な目標は、セッターメソッドを記述したり@Autowired
ファイルを使用したりせずに、そのapplicationContext-test.xml
変数にモックオブジェクトを配置するJavaコードまたは注釈を作成することです。 。テストのためだけに個別のアプリケーションコンテンツを維持するのではなく、.Java
ファイルでテストケースのすべてを維持したいです。
モックオブジェクトにMockitoを使用したいと考えています。過去にorg.mockito.Mockito
を使用し、Mockito.mock(MyClass.class)
でオブジェクトを作成することでこれを実行しました。
テストでMyLauncherにモックを絶対に注入できます。あなたが誰かを使用しているモックフレームワークを示したら、答えをすぐに提供できると確信しています。 mockitoを使用して、@ RunWith(MockitoJUnitRunner.class)の使用とmyLauncherの注釈の使用を検討します。以下のようになります。
@RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
@InjectMocks
private MyLauncher myLauncher = new MyLauncher();
@Mock
private MyService myService;
@Test
public void someTest() {
}
}
受け入れられた答え(MockitoJUnitRunner
と@InjectMocks
を使用)は素晴らしいです。しかし、少しの軽量化(特別なJUnitランナーなし)、特に不定期の使用のために「魔法的」ではない(より透明な)ものが必要な場合は、イントロスペクションを使用してプライベートフィールドを直接設定できます。
Springを使用している場合は、このためのユーティリティクラスが既にあります。 org.springframework.test.util.ReflectionTestUtils
使い方はとても簡単です:
ReflectionTestUtils.setField(myLauncher, "myService", myService);
最初の引数はターゲットBean、2番目は(通常はプライベート)フィールドの名前、最後は注入する値です。
Springを使用しない場合、このようなユーティリティメソッドを実装するのは非常に簡単です。このSpringクラスを見つける前に使用したコードは次のとおりです。
public static void setPrivateField(Object target, String fieldName, Object value){
try{
Field privateField = target.getClass().getDeclaredField(fieldName);
privateField.setAccessible(true);
privateField.set(target, value);
}catch(Exception e){
throw new RuntimeException(e);
}
}
場合によっては、@Component
をリファクタリングして、コンストラクターまたはセッターベースのインジェクションを使用してテストケースをセットアップできます(@Autowired
に依存できます)。これで、代わりにテストスタブを実装することにより、モックフレームワークなしでテストを完全に作成できます(例:Martin Fowlerの MailServiceStub ):
@Component
public class MyLauncher {
private MyService myService;
@Autowired
MyLauncher(MyService myService) {
this.myService = myService;
}
// other methods
}
public class MyServiceStub implements MyService {
// ...
}
public class MyLauncherTest
private MyLauncher myLauncher;
private MyServiceStub myServiceStub;
@Before
public void setUp() {
myServiceStub = new MyServiceStub();
myLauncher = new MyLauncher(myServiceStub);
}
@Test
public void someTest() {
}
}
この手法は、テストとテスト対象のクラスが同じパッケージにある場合に特に役立ちます。デフォルトの package-private アクセス修飾子を使用して、他のクラスがアクセスできないようにするためです。 src/main/Java
には本番コードを保持できますが、src/main/test
ディレクトリにはテストを保持できます。
Mockitoが好きなら、 MockitoJUnitRunner に感謝します。 @Manuelが示したような「魔法の」ことをすることができます:
@RunWith(MockitoJUnitRunner.class)
public class MyLauncherTest
@InjectMocks
private MyLauncher myLauncher; // no need to call the constructor
@Mock
private MyService myService;
@Test
public void someTest() {
}
}
または、デフォルトのJUnitランナーを使用して、setUp()
メソッドで MockitoAnnotations.initMocks() を呼び出して、Mockitoに注釈付きの値を初期化させることができます。詳細については、javadocの @ InitMocks と、私が書いた ブログ投稿 を参照してください。
MyLauncherクラスで自動配線を機能させるには(myServiceの場合)、myLauncherを自動配線して、コンストラクターを呼び出すのではなく、Springで初期化する必要があります。それが自動配線されると(そしてmyServiceも自動配線されます)、Spring(1.4.0以降)は、テストに配置できる@MockBeanアノテーションを提供します。これにより、コンテキスト内の一致する単一のBeanがそのタイプのモックに置き換えられます。その後、@ Beforeメソッドで、必要なモックをさらに定義できます。
public class MyLauncherTest
@MockBean
private MyService myService;
@Autowired
private MyLauncher myLauncher;
@Before
private void setupMockBean() {
doNothing().when(myService).someVoidMethod();
doReturn("Some Value").when(myService).someStringMethod();
}
@Test
public void someTest() {
myLauncher.doSomething();
}
}
これで、MyLauncherクラスは変更されないままになり、MyService Beanは、定義したとおりにメソッドが値を返すモックになります。
@Component
public class MyLauncher {
@Autowired
MyService myService;
public void doSomething() {
myService.someVoidMethod();
myService.someMethodThatCallsSomeStringMethod();
}
//other methods
}
言及された他の方法に対するこの利点のいくつかは、次のとおりです。
これを見てください link
次に、テストケースを
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"/applicationContext.xml"})
public class MyLauncherTest{
@Resource
private MyLauncher myLauncher ;
@Test
public void someTest() {
//test code
}
}
私はSpringの新しいユーザーです。これに対して別の解決策を見つけました。リフレクションを使用して必要なフィールドを公開し、モックオブジェクトを割り当てます。
これは私の認証コントローラーであり、いくつかのAutowiredプライベートプロパティがあります。
@RestController
public class AuthController {
@Autowired
private UsersDAOInterface usersDao;
@Autowired
private TokensDAOInterface tokensDao;
@RequestMapping(path = "/auth/getToken", method = RequestMethod.POST)
public @ResponseBody Object getToken(@RequestParam String username,
@RequestParam String password) {
User user = usersDao.getLoginUser(username, password);
if (user == null)
return new ErrorResult("Kullanıcıadı veya şifre hatalı");
Token token = new Token();
token.setTokenId("aergaerg");
token.setUserId(1);
token.setInsertDatetime(new Date());
return token;
}
}
そして、これはAuthControllerの私のJunitテストです。私はパブリックに必要なプライベートプロパティを作成し、モックオブジェクトをそれらに割り当て、ロックします:)
public class AuthControllerTest {
@Test
public void getToken() {
try {
UsersDAO mockUsersDao = mock(UsersDAO.class);
TokensDAO mockTokensDao = mock(TokensDAO.class);
User dummyUser = new User();
dummyUser.setId(10);
dummyUser.setUsername("nixarsoft");
dummyUser.setTopId(0);
when(mockUsersDao.getLoginUser(Matchers.anyString(), Matchers.anyString())) //
.thenReturn(dummyUser);
AuthController ctrl = new AuthController();
Field usersDaoField = ctrl.getClass().getDeclaredField("usersDao");
usersDaoField.setAccessible(true);
usersDaoField.set(ctrl, mockUsersDao);
Field tokensDaoField = ctrl.getClass().getDeclaredField("tokensDao");
tokensDaoField.setAccessible(true);
tokensDaoField.set(ctrl, mockTokensDao);
Token t = (Token) ctrl.getToken("test", "aergaeg");
Assert.assertNotNull(t);
} catch (Exception ex) {
System.out.println(ex);
}
}
}
この方法の長所と短所はわかりませんが、これは機能しています。このテクニックにはもう少しコードがありますが、これらのコードはさまざまな方法などで分離できます。この質問にはさらに良い答えがありますが、別の解決策を示したいと思います。悪い英語でごめんなさい。みんなに良いJavaを持っている:)