web-dev-qa-db-ja.com

同じインターフェースの複数のモックを注入する方法

私がテストしたいJavaクラス(ServiceCallerと呼ばれる))はこれを持っています:

_@Autowired @Qualifier(value="serviceA")
SomeService serviceA;

@Autowired @Qualifier(value="serviceB")
SomeService serviceB;
_

(条件をチェックしてAまたはBを呼び出すdoWork()メソッドがあります)。

各サービスのモックを適切な変数に挿入するにはどうすればよいですか?

私の Junit にはこれがあります:

_@InjectMocks ServiceCaller classUnderTest = new ServiceCaller();

@Mock SomeService mockServiceA;
@Mock SomeService mockServiceB;
_

しかし、テストを実行して、サービスA/Bが正しい条件で呼び出されていることを確認すると、モックが挿入されていないため、nullポインターが表示されます。

同じインターフェース(SomeService)に複数の依存関係があることは明らかです。モックサービスを宣言するときに修飾子を指定する方法はありますか?または、依存関係のセッターを用意して、昔ながらの方法を設定する必要がありますか?

20
shuttsy

モックにserviceAおよびserviceBという名前を付けるだけで十分です。 Mockitoから ドキュメント

プロパティセッターインジェクション;モックは最初にタイプで解決され、次に同じタイプのプロパティが複数ある場合は、プロパティ名とモック名の一致によって解決されます。

あなたの例では:

@InjectMocks ServiceCaller classUnderTest;

@Mock SomeService serviceA;
@Mock SomeService serviceB;

@InjectMocksを使用する場合は、クラスインスタンスを手動で作成する必要がないことに注意してください。

それにもかかわらず、私は個人的にコンストラクタを使用して依存関係を注入することを好みます。テストにモックを挿入するのが簡単になります(モックでコンストラクターを呼び出すだけです-反射ツールや@InjectMocksは不要です(これは便利ですが、いくつかの側面を隠します))。さらに [〜#〜] tdd [〜#〜] を使用すると、テストしたクラスに必要な依存関係が明確に表示され、さらにIDEでコンストラクタスタブを生成できます。

Spring Frameworkはコンストラクターインジェクションを完全にサポートしています。

@Bean
public class ServiceCaller {
    private final SomeService serviceA;
    private final SomeService serviceB;

    @Autowired
    public ServiceCaller(@Qualifier("serviceA") SomeService serviceA,
                         @Qualifier("serviceB") SomeService serviceB) { ... }

    ...
}

このコードは以下でテストできます:

@Mock SomeService serviceA;
@Mock SomeService serviceB;

//in a setup or test method
ServiceCaller classUnderTest = new ServiceCaller(serviceA, serviceB); 
21

「name」プロパティを使用して、次のようにインスタンスを定義できます。

@Mock(name="serviceA") SomeService serviceA;
@Mock(name="serviceB") SomeService serviceB;
8
osiris256

同じタイプの依存関係がある場合、mockitoは同じタイプのプロパティが原因で依存関係の注入を停止します。次の方法で@ osiris256を参照してこれを解決するには:

class ServiceLayer{

   @Autowired
   @Qualifier("bean1")
   private InterfaceA typeA;  

   @Autowired
   @Qualifier("bean2")
   private InterfaceA typeB;  

}

テストクラスは次のようになります。

@RunWith(SpringRunner.class)    
class ServiceLayerTest{

  @Mock(name = "typeA")
  private InterfaceA typeA;  

  @Mock(name = "typeB")
  private InterfaceA typeB;  

  @InjectMocks
  ServiceLayer serviceLayer;

  @Before
  public void initialiseBeforeTest(){
     MockitoAnnotations.initMocks(this);
  }

  // here goes your test 
  @Test
   public void test(){
     // use your when then .....
  }
}

注:SpringRunnerを使用していて@MockBeanを使用している場合、これは機能しません。@ osiris256を参照して、@ Mock(name = "")に置き換える必要があります。

1
Abhijeet Ranade