web-dev-qa-db-ja.com

Factoryクラスによって作成されたオブジェクトにモックを挿入する

私は次のクラスを持っています:

public class MyClass {        
    private Apple apple;

    public void myMethod() {
       Apple = AppleFactory.createInstance(someStringVariable);
       ....
       ....
       ....
    }
}

そしてTestクラス:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

        @InjectMocks 
        MyClass myClass;

        @Test
        public void myMethod(){
         ...
         ...
         ...
        }
    }

MyClassのモックとしてAppleインスタンスを注入するにはどうすればよいですか?

15
saravana_pc

これを解決するには3つの方法があります。

Abstract factory:静的メソッドを使用する代わりに、具象ファクトリクラスを使用します。

public abstract class AppleFactory {
    public Apple createInstance(final String str);
}

public class AppleFactoryImpl implements AppleFactory {
    public Apple createInstance(final String str) { // Implementation }
}

テストクラスで、ファクトリーをモックします。

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    private AppleFactory appleFactoryMock;

    @Mock
    private Apple appleMock;

    @InjectMocks 
    MyClass myClass;

    @Before
    public void setup() {
        when(appleFactoryMock.createInstance(Matchers.anyString()).thenReturn(appleMock);
    }

    @Test
    public void myMethod(){
     ...
     ...
     ...
    }
}

PowerMock:静的メソッドのモックを作成するには、PowerMockを使用します。 関連する質問に対する私の回答 を見て、それがどのように行われるかを確認してください。

テスト可能なクラスAppleの作成をprotectedメソッドでラップし、それをオーバーライドするテストクラスを作成します。

public class MyClass {
   private Apple apple;

   public void myMethod() {
       Apple = createApple();
       ....
       ....
       ....
   }

   protected Apple createApple() {
       return AppleFactory.createInstance(someStringVariable);
   }
}


@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    private Apple appleMock;

    @InjectMocks 
    MyClass myClass;

    @Test
    public void myMethod(){
     ...
     ...
     ...
    }

    private class TestableMyClass extends MyClass {
       @Override
       public void createApple() {
          return appleMock;
       }
    }
}

もちろん、テストクラスではTestableMyClassではなくMyClassをテストする必要があります。

それぞれの方法について私の意見をお話しします。

  1. 抽象ファクトリメソッドが最適です-これは、実装の詳細を隠す明確な設計です

  2. テスト可能なクラス-最小限の変更を必要とする2番目のオプションです

  3. PowerMockオプションは私の一番のお気に入りではありません-より良い設計を行う代わりに、問題を無視して隠します。しかし、それはまだ有効なオプションです。
22
Avi

Avi&Ev0oDの最初の回答について。抽象クラスは拡張のみが可能で、実装はできません。

  public abstract class AppleFactory {
    public abstract Apple createInstance(final String str);
}

public class AppleFactoryImpl extends AppleFactory {
    public Apple createInstance(final String str) { // Implementation }
}
2
ugurkocak1980

Aviが提案したソリューションに加えて、4番目の可能性を選択できます。

インジェクト・ファクトリー:これは、私にとって、refacrotするコードがすでにある場合の最良のオプションです。このソリューションでは、Porductionコードを変更する必要はなく、ファクトリクラスとテストを変更するだけです。

public class AppleFactory
{
    private static Apple _injectedApple;

    public static createInstance(String str)
    {
        if (_injectedApple != null)
        {
            var currentApple = _injectedApple;
            _injectedApple = null;
            return currentApple;
        }

        //standard implementation
    }

    public static setInjectedApple(Apple apple)
    {
        _injectedApple = Apple;
    }
}

これで、静的ファクトリーを簡単に使用できます。

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock
    private Apple appleMock;

    @InjectMocks 
    MyClass myClass;

    @Before
    public void setup() {
        AppleFactory.setInjectedApple(appleMock);
    }

    @Test
    public void myMethod(){
     ...
     ...
     ...
    }
}
0
Glauco Cucchiar