1

自動配線された最終クラス オブジェクトと、@PostConstruct メソッドを持つ別の自動配線されたクラスを使用して、Java クラスを単体テストしたいと考えています。それらを個別にテストすることは可能ですが、それらを組み合わせることはできません。

この質問は、mockito モックを spring Bean に注入することに関する質問の拡張です

テストするコード

public class A { 
    @Autowired
    private FinalClass serviceClient;
    @Autowired
    private ClassWithPostConstructor resourceVerifier;

    //no setters or constructors

    public String useBothFinalClassAndClassWithPostConstructor() {
        //logic to be tested
    }
}

ワーキングテストクラス

@RunWith(PowerMockRunner.class)
@PrepareForTest(FinalClass.class)
public class ATest {
    //@org.mockito.Mock //fails to mock final class
    @org.powermock.api.easymock.annotation.Mock
    private FinalClass serviceClient;
    @org.powermock.api.easymock.annotation.Mock
    private ClassWithPostConstructor resourceVerifier;
    //other mock objects required for mocking the services

    //@InjectMocks //fails since mocking final class
    private A a;

    @Before
    public void init() {
        a = new A();
        //working snippet with setters created in A and without @Autowired here within the test
        serviceClient = PowerMock.create(FinalClass.class);
        a.setServiceClient(serviceClient);
        resourceVerifier = PowerMock.create(ClassWithPostConstructor.class);
        a.setClassWithPostConstructor(resourceVerifier);
    }

    @Test
    public void testTheMethodUsingExpectAndVerify() {
        //test the functionality here
        EasyMock.expect(serviceClient.callService()).andReturn("someMock");
        EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
        PowerMock.replayAll();
        A.useBothFinalClassAndClassWithPostConstructor();
        PowerMock.verifyAll();
    }
}

上記のコードは、ファイル内のセッターの必要性に対応しています

期待されるテストクラス

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"file:spring-configuration/unit-testing-config.xml"})
@PrepareForTest(FinalClass.class)
public class ATest {
    @Autowired
    private FinalClass serviceClient;
    @Autowired
    private ClassWithPostConstructor resourceVerifier;
    //other mock objects required for mocking the services

    private A a;

    @Before
    public void init() {
        a = new A();
    }

    @Test
    public void testTheMethodUsingExpectAndVerify() {
        //test the functions here
        EasyMock.expect(serviceClient.callService()).andReturn("someMock");
        EasyMock.expect(resourceVerifier.verifyFn()).andReturn("success");
        PowerMock.replayAll();
        A.useBothFinalClassAndClassWithPostConstructor();
        PowerMock.verifyAll();
    }
}

//spring-configuration/unit-testing-config.xml
//same error even on customer factory
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
    <constructor-arg type="java.lang.Class" value="com.company...resourceVerifier" /> 
</bean>
<bean id="resourceVerifier" class="org.powermock.api.easymock.PowerMock" factory-method="createMock">
    <constructor-arg type="java.lang.Class" value="com.company...serviceClient" /> 
</bean>

上記のスニペットは finalClass をモックしますが、ResourceVerifier.class の @PostConstructor を呼び出します - この呼び出しを克服するには、ここで何をする必要があります。


調査

  • @InjectMocksを使用して、Springコンテキスト構成を必要とせずに自動配線されたファイルをテストすることができます。
  • @InjectMockは static および final フィールドに対してサイレントに失敗し、失敗すると他のモックも注入されません。
  • PowerMockの createMockを使用して最終クラスをモックし、 PowerMockRunnerおよび@PrepareForTestを使用してテストを実行することができます。ただし、これには@Autowiredフィールドのモックを挿入するための新しい不要なセッターが必要です。
  • MockitoAnnotations.@Mockは PowerMock とうまく連携できず (特に最終クラス オブジェクトをモックする場合)、EasyMock.Annotations.@Mockで解決できます。
  • EasyMockPowerMockには、Mockito によって可能な限りモックを注入するための@InjectMocksアノテーションがありません(問題を数秒で解決できたはずです)。
  • SpringJUnit4Runnerと別の単体テスト@ContextConfigurationを介して自動配線された Spring Bean を注入することが可能です。
  • PowerMockRunnerPowerMockRunnerDelegateを使用したSpringJUnit4Runnerの両方で同じテスト ファイルを実行することが可能です。
  • @PostConstructメソッドは、Spring Bean の作成と注入を使用するよりもコードでモックされた場合、自動的に実行されないことを知っています。
  • ラッパー ファクトリ Bean クラスが作成され、モックの作成に使用される場合、自動的に挿入されますが、それと共に @PostConstruct メソッドも呼び出されます。
  • Springockito は現時点では信頼できないため、これに依存することはできません。

しかし、ユースケースはこれらすべての組み合わせであるため、これらのどれも機能しませんでした。


可能な解決策

  • @Autowiredフィールドを削除し、 Setter インジェクションを使用して、PowerMock を使用して通常どおりモックすることで可能にします (動作確認済み) - しかし、これは外部チーム パッケージが従う規則です - 私はそれに固執するために最善を尽くす必要があります。
  • または @Autowired をセッターまたはコンストラクターに設定します

代替案?

クラスは目的を果たし、適切に設計されているため、クラスを再編成する必要はないと思います。

  • テスト中のクラスを操作する必要のないその他の手段 - このクラスを変更する権限がない場合はどうなりますか? つまり、純粋なテスト ライブラリ依存ソリューションです。
  • PowerMockitoで可能かどうかわかりませんか? PowerMockito と PowerMockの組み合わせは試していません。
4

1 に答える 1

1

うーん、

PowerMockito で可能かどうかわかりませんか? PowerMockito と PowerMock の組み合わせは試していません。

頭が混乱していて、PowerMock/Mockito と EasyMock を誤解しているようです。

PowerMockitoとを同時に使用しないでくださいPowerMock。これら 2 つのクラスは、2 つの異なるモッキング フレームワーク ( EasyMockMockito )の PowerMock に適した API であるためです。そして、両方を使用する理由はありません。

そしてもちろん、これは仕事が欲しい

//@org.mockito.Mock //fails to mock final class
@org.powermock.api.easymock.annotation.Mock
private FinalClass serviceClient;
@org.powermock.api.easymock.annotation.Mock
private ClassWithPostConstructor resourceVerifier;
//other mock objects required for mocking the services

//@InjectMocks //fails since mocking final class
private A a;

なぜなら、EasyMock API を介して減速してモックを作成しますが、Mockito Annotation を使用して注入しようとするからです。

Mocking Framework を 1 つだけ選択し、それに適した API を使用する必要があります。今のところ、Mockito + PowerMockito (Mockito の PowerMock API) の方が要件に適しています。

PowerMock githubでどのように機能するかを完全な例で示すことができます

@RunWith(PowerMockRunner.class)
@PrepareForTest(FinalClass.class)
public class SpringInjectFinalClassExampleTest {

    @Mock
    private FinalClass finalClass;

    @InjectMocks
    private MyBean myBean = new MyBean();;

    @Test
    public void testInjectFinalClass() {
        final String value = "What's up?";
        when(finalClass.sayHello()).thenReturn(value);

        assertEquals(value, myBean.sayHello());

    }

}
于 2016-04-23T18:38:31.820 に答える