自動配線された最終クラス オブジェクトと、@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で解決できます。
- EasyMockとPowerMockには、Mockito によって可能な限りモックを注入するための@InjectMocksアノテーションがありません(問題を数秒で解決できたはずです)。
- SpringJUnit4Runnerと別の単体テスト@ContextConfigurationを介して自動配線された Spring Bean を注入することが可能です。
- PowerMockRunnerとPowerMockRunnerDelegateを使用したSpringJUnit4Runnerの両方で同じテスト ファイルを実行することが可能です。
- @PostConstructメソッドは、Spring Bean の作成と注入を使用するよりもコードでモックされた場合、自動的に実行されないことを知っています。
- ラッパー ファクトリ Bean クラスが作成され、モックの作成に使用される場合、自動的に挿入されますが、それと共に @PostConstruct メソッドも呼び出されます。
- Springockito は現時点では信頼できないため、これに依存することはできません。
しかし、ユースケースはこれらすべての組み合わせであるため、これらのどれも機能しませんでした。
可能な解決策
- @Autowiredフィールドを削除し、 Setter インジェクションを使用して、PowerMock を使用して通常どおりモックすることで可能にします (動作確認済み) - しかし、これは外部チーム パッケージが従う規則です - 私はそれに固執するために最善を尽くす必要があります。
- または @Autowired をセッターまたはコンストラクターに設定します
代替案?
クラスは目的を果たし、適切に設計されているため、クラスを再編成する必要はないと思います。
- テスト中のクラスを操作する必要のないその他の手段 - このクラスを変更する権限がない場合はどうなりますか? つまり、純粋なテスト ライブラリ依存ソリューションです。
- PowerMockitoで可能かどうかわかりませんか? PowerMockito と PowerMockの組み合わせは試していません。