web-dev-qa-db-ja.com

Mockito Spyオブジェクトにモックを挿入する

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
    }

}
20
Wildchild

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

}
10
Sergii Bishyr

私はまた、モックをスパイに注入する方法をさまよいました。

次のアプローチは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);
}

(ここでSubjectUnderTestMySpyに依存し、MySpyNeedToBeMockedに依存します)。

PD:個人的には、そのような魔法をあまりにも頻繁に行う必要がある場合、クラス間の依存関係に何らかの問題があることを示している可能性があり、少し実行する価値があると思いますコードを改善するためのリファクタリング。

23
Yoory N.

また、Springブートフレームワークを使用した単体テスト中にこの問題に遭遇しましたが、@ Spyと@InjectMocksの両方を使用するための1つのソリューションを見つけました。

Yoory Nからの以前の回答

@Spy
@InjectMocks
private MySpy spy;

InjectMocksではインスタンスを作成する必要があるため、ソリューションは以下のとおりです。

@Spy
@InjectMocks
private MySpy spy = new MySpy();
6
Jack Yang

決定的な答えを見つけたと思う。 Yooryアプローチを試しましたが、注釈の順序を変更しました。

@InjectMocks
@Spy
private MySpy spy;

Mockitoは最初にモックを作成し、その上にスパイを追加すると仮定します。したがって、MySpyオブジェクトをインスタンス化する必要はありません。

1
DavidBu