2レベルの依存性注入があるクラスのテストケースを書いています。 1レベルの依存性注入オブジェクトに@Spyアノテーションを使用し、2番目のレベルの注入をモックしたいと思います。ただし、第2レベルでNULLポインター例外を取得し続けました。モックを@Spyオブジェクトに注入する方法はありますか?
public class CarTestCase{
@Mock
private Configuration configuration;
@Spy
private Engine engine;
@InjectMocks
private Car car;
@Test
public void test(){
Mockito.when(configuration.getProperties("")).return("Something");
car.drive();
}
}
public class Car{
@Inject
private Engine engine;
public void drive(){
engine.start();
}
}
public class Engine{
@Inject
private Configuration configuration;
public void start(){
configuration.getProperties(); // null pointer exception
}
}
Mockitoは注入フレームワークではないため、このようなトリッキーな注入を実行できません。そのため、テストしやすくするためにコードをリファクタリングする必要があります。コンストラクターインジェクションを使用して簡単に実行できます。
public class Engine{
private Configuration configuration;
@Inject
public Engine(Configuration configuration) {
this.configuration = configuration;
}
........
}
public class Car{
private Engine engine;
@Inject
public Car(Engine engine) {
this.engine = engine;
}
}
この場合、モックとインジェクションを手動で処理する必要があります。
public class CarTestCase{
private Configuration configuration;
private Engine engine;
private Car car;
@Before
public void setUp(){
configuration = mock(Configuration.class);
engine = spy(new Engine(configuration));
car = new Car(engine);
}
@Test
public void test(){
Mockito.when(configuration.getProperties("")).return("Something");
car.drive();
}
}
私はまた、モックをスパイに注入する方法をさまよいました。
次のアプローチはnot動作します:
@Spy
@InjectMocks
private MySpy spy;
ただし、注釈と手動のモックの両方を使用する場合、「ハイブリッド」アプローチによって望ましい動作を実現できます。以下は完全に機能します:
@Mock
private NeedToBeMocked needToBeMocked;
@InjectMocks
private MySpy mySpy;
@InjectMocks
private SubjectUnderTest sut;
@BeforeMethod
public void setUp() {
mySpy = Mockito.spy(new MySpy());
MockitoAnnotations.initMocks(this);
}
(ここでSubjectUnderTest
はMySpy
に依存し、MySpy
はNeedToBeMocked
に依存します)。
PD:個人的には、そのような魔法をあまりにも頻繁に行う必要がある場合、クラス間の依存関係に何らかの問題があることを示している可能性があり、少し実行する価値があると思いますコードを改善するためのリファクタリング。
また、Springブートフレームワークを使用した単体テスト中にこの問題に遭遇しましたが、@ Spyと@InjectMocksの両方を使用するための1つのソリューションを見つけました。
Yoory Nからの以前の回答
@Spy
@InjectMocks
private MySpy spy;
InjectMocksではインスタンスを作成する必要があるため、ソリューションは以下のとおりです。
@Spy
@InjectMocks
private MySpy spy = new MySpy();
決定的な答えを見つけたと思う。 Yooryアプローチを試しましたが、注釈の順序を変更しました。
@InjectMocks
@Spy
private MySpy spy;
Mockitoは最初にモックを作成し、その上にスパイを追加すると仮定します。したがって、MySpyオブジェクトをインスタンス化する必要はありません。