違いは何である@Mock
と@InjectMocks
Mockitoフレームワークでは?
違いは何である@Mock
と@InjectMocks
Mockitoフレームワークでは?
回答:
@Mock
モックを作成します。@InjectMocks
クラスのインスタンスを作成し、@Mock
(または@Spy
)アノテーションで作成されたモックをこのインスタンスに注入します。
これらのモックを初期化して注入するには、@RunWith(MockitoJUnitRunner.class)
またはMockito.initMocks(this)
を使用する必要があることに注意してください。
@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {
@InjectMocks
private SomeManager someManager;
@Mock
private SomeDependency someDependency; // this will be injected into someManager
//tests...
}
これは、方法@Mock
と@InjectMocks
動作に関するサンプルコードです。
クラスがあるGame
としPlayer
ます。
class Game {
private Player player;
public Game(Player player) {
this.player = player;
}
public String attack() {
return "Player attack with: " + player.getWeapon();
}
}
class Player {
private String weapon;
public Player(String weapon) {
this.weapon = weapon;
}
String getWeapon() {
return weapon;
}
}
ご覧のとおり、Game
クラスはPlayer
を実行する必要がありますattack
。
@RunWith(MockitoJUnitRunner.class)
class GameTest {
@Mock
Player player;
@InjectMocks
Game game;
@Test
public void attackWithSwordTest() throws Exception {
Mockito.when(player.getWeapon()).thenReturn("Sword");
assertEquals("Player attack with: Sword", game.attack());
}
}
MockitoはPlayerクラスをモックし、その動作はメソッドwhen
とthenReturn
メソッドを使用します。最後に、@InjectMocks
Mockitoを使用すると、それがに入れPlayer
られGame
ます。
new Game
オブジェクトを作成する必要さえないことに注意してください。Mockitoがそれを注入します。
// you don't have to do this
Game game = new Game(player);
@Spy
アノテーションを使用しても同じ動作が得られます。属性名が異なっていても。
@RunWith(MockitoJUnitRunner.class)
public class GameTest {
@Mock Player player;
@Spy List<String> enemies = new ArrayList<>();
@InjectMocks Game game;
@Test public void attackWithSwordTest() throws Exception {
Mockito.when(player.getWeapon()).thenReturn("Sword");
enemies.add("Dragon");
enemies.add("Orc");
assertEquals(2, game.numberOfEnemies());
assertEquals("Player attack with: Sword", game.attack());
}
}
class Game {
private Player player;
private List<String> opponents;
public Game(Player player, List<String> opponents) {
this.player = player;
this.opponents = opponents;
}
public int numberOfEnemies() {
return opponents.size();
}
// ...
MockitoがチェックするためだType Signature
あるゲームのクラス、のPlayer
とList<String>
。
テストクラスでは、テストするクラスにで注釈を付ける必要があり@InjectMocks
ます。これは、モックをどのクラスに注入するかをMockitoに指示します。
@InjectMocks
private SomeManager someManager;
それ以降、クラス内の特定のメソッドまたはオブジェクト(この場合はSomeManager
)をモックに置き換えるように指定できます。
@Mock
private SomeDependency someDependency;
この例でSomeDependency
は、SomeManager
クラス内がモックされます。
@Mock
アノテーションは関係するオブジェクトを模倣します。
@InjectMocks
アノテーションを使用すると、によって作成されたさまざまな(および関連する)モックを基礎となるオブジェクトに注入でき@Mock
ます
どちらも補完的です。
@InjectMocks
このクラスを構築してそれをスパイするのにも使用することを考えていました
例えば
@Mock
StudentDao studentDao;
@InjectMocks
StudentService service;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
}
ここでは、サービスクラスのDAOクラスが必要です。そこで、それをモックして、サービスクラスインスタンスに挿入します。同様に、Springフレームワークでは、すべての@Autowired BeanをjUnitsの@Mockでモックし、@ InjectMocksを介してBeanに注入できます。
MockitoAnnotations.initMocks(this)
メソッドは、これらのモックを初期化し、すべてのテストメソッドに対してインジェクトするため、setUp()
メソッドで呼び出す必要があります。
Mockitoのベースとなっている「モッキングフレームワーク」は、モックオブジェクトを作成するためのフレームワークです(以前の用語では、これらのオブジェクトは、依存機能のシャントとして機能するため、シャントと呼ばれることがありました)。つまり、モックオブジェクトは、コードが依存する実際のオブジェクトを模倣するために使用され、モックフレームワークでプロキシオブジェクトを作成します。テストでモックオブジェクトを使用すると、基本的に通常の単体テストから統合テストに移行します
MockitoはMITライセンスの下でリリースされたJava用のオープンソースのテストフレームワークで、「モックフレームワーク」であり、クリーンでシンプルなAPIを使用して美しいテストを記述できます。Java空間にはさまざまなモックフレームワークがありますが、モックオブジェクトフレームワークには、主に2つのタイプがあります。1つはプロキシを介して実装され、もう1つはクラスの再マッピングを介して実装されます。
Springなどの依存性注入フレームワークを使用すると、コードを変更せずにプロキシオブジェクトを注入できます。モックオブジェクトは特定のメソッドが呼び出されることを想定しており、期待される結果を返します。
@InjectMocks
注釈は、注釈付きでテストオブジェクトのインスタンスと注入フィールドインスタンス化しようとし@Mock
たり@Spy
、テストオブジェクトのプライベートフィールドにします。
MockitoAnnotations.initMocks(this)
呼び出し、テストオブジェクトをリセットし、モックを再初期化するので、これを@Before
/ @BeforeMethod
アノテーションに忘れずに置いてください。
@Tomで言及されているアプローチで得られる利点の1つは、SomeManagerでコンストラクターを作成する必要がないため、クライアントがインスタンス化できるように制限することです。
@RunWith(MockitoJUnitRunner.class)
public class SomeManagerTest {
@InjectMocks
private SomeManager someManager;
@Mock
private SomeDependency someDependency; // this will be injected into someManager
//You don't need to instantiate the SomeManager with default contructor at all
//SomeManager someManager = new SomeManager();
//Or SomeManager someManager = new SomeManager(someDependency);
//tests...
}
これが良い習慣かどうかは、アプリケーションの設計によって異なります。
多くの人々が@Mock
vs についてここで素晴らしい説明をしました@InjectMocks
。私はそれが好きですが、私たちのテストとアプリケーションは私たちが使用する必要がないように書かれるべきだと思います@InjectMocks
。
例と一緒にさらに読むためのリファレンス:https : //tedvinke.wordpress.com/2014/02/13/mockito-why-you-should-not-use-injectmocks-annotation-to-autowire-fields/
@InjectMocksアノテーションを使用すると、モックフィールドをテストオブジェクトに自動的に挿入できます。
以下の例では、@ InjectMocksを使用して、モックdataMapをdataLibraryに挿入しています。
@Mock
Map<String, String> dataMap ;
@InjectMocks
DataLibrary dataLibrary = new DataLibrary();
@Test
public void whenUseInjectMocksAnnotation_() {
Mockito.when(dataMap .get("aData")).thenReturn("aMeaning");
assertEquals("aMeaning", dataLibrary .getMeaning("aData"));
}
廃止予定で@InjectMocks
あることを確認します
@InjectMocksの廃止とMockito 3/4での削除のスケジュール
フィールドを自動配線するためにInjectMocksアノテーションを使用すべきでない理由
上記の回答はカバーしていますが、欠けていると思われる詳細な詳細を追加しようとしました。それらの背後にある理由(なぜ)。
図:
Sample.java
---------------
public class Sample{
DependencyOne dependencyOne;
DependencyTwo dependencyTwo;
public SampleResponse methodOfSample(){
dependencyOne.methodOne();
dependencyTwo.methodTwo();
...
return sampleResponse;
}
}
SampleTest.java
-----------------------
@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassA.class})
public class SampleTest{
@InjectMocks
Sample sample;
@Mock
DependencyOne dependencyOne;
@Mock
DependencyTwo dependencyTwo;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
public void sampleMethod1_Test(){
//Arrange the dependencies
DependencyResponse dependencyOneResponse = Mock(sampleResponse.class);
Mockito.doReturn(dependencyOneResponse).when(dependencyOne).methodOne();
DependencyResponse dependencyTwoResponse = Mock(sampleResponse.class);
Mockito.doReturn(dependencyOneResponse).when(dependencyTwo).methodTwo();
//call the method to be tested
SampleResponse sampleResponse = sample.methodOfSample()
//Assert
<assert the SampleResponse here>
}
}