Mockitoフレームワークの@Mock
と@InjectMocks
の違いは何ですか?
@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
を使うことで、その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がGameクラスのType Signature
、つまりPlayer
とList<String>
をチェックするためです。
あなたのテストクラスでは、テストされたクラスは@InjectMocks
でアノテーションを付けられるべきです。これはMockitoにモックを注入するクラスを指示します。
@InjectMocks
private SomeManager someManager;
それ以降は、クラス内のどの特定のメソッドまたはオブジェクト(この場合はSomeManager
)をモックで置き換えるかを指定できます。
@Mock
private SomeDependency someDependency;
この例では、SomeDependency
クラス内のSomeManager
がモックされます。
@Mock
アノテーションは、関係するオブジェクトを偽造します。
@InjectMocks
アノテーションは、@Mock
によって作成された異なる(そして関連する)モックを基礎となるオブジェクトに注入することを可能にします。
両方とも補完的です。
例えば
@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のベースとなっている「モックフレームワーク」は、モックオブジェクトを作成する機能を提供するフレームワークです(古い機能では、これらは依存機能のためのシャントとして機能するため、シャントと呼ばれることがありました)。 objectはあなたのコードが依存している実際のオブジェクトを模倣するのに使われます、あなたはモックフレームワークでプロキシオブジェクトを作成します。テストにモックオブジェクトを使用することで、基本的に通常の単体テストから統合テストに移行します。
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/ /
@Mock
は、依存Beanの参照を宣言/モックするために使用され、@InjectMocks
は、テストが作成されているBeanをモックするために使用されます。
例えば:
public class A{
public class B b;
public void doSomething(){
}
}
クラスA
のテスト:
public class TestClassA{
@Mocks
public class B b;
@InjectMocks
public class A a;
@Test
public testDoSomething(){
}
}
@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"));
}