質問
いくつかのテストでモッキートを使用しています。
私は次のクラスを持っています:
class BaseService {
public void save() {...}
}
public Childservice extends BaseService {
public void save(){
//some code
super.save();
}
}
2回目の呼び出しだけをock笑したい(super.save
) の ChildService
. 。最初の呼び出しは、実際の方法を呼び出す必要があります。それをする方法はありますか?
解決
いいえ、モキトはこれをサポートしていません。
これはあなたが探している答えではないかもしれませんが、あなたが見ているのは、設計原則を適用しないという症状です。
スーパークラスを拡張する代わりに戦略を抽出すると、問題はなくなります。
ただし、コードを変更することは許可されていないが、とにかくテストする必要があり、この厄介な方法では、まだ希望があります。一部のAOPツール(AspectJなど)を使用すると、コードをスーパークラスメソッドに織り込み、その実行を完全に回避できます(Yuck)。プロキシを使用している場合、これは機能しません。ByteCode変更(ロードタイム織りまたはコンパイル時間織りのいずれか)を使用する必要があります。 PowerMockやPowerMockitoなど、このタイプのトリックもサポートするモッキングフレームワークがあります。
私はあなたがリファクタリングに行くことをお勧めしますが、それがあなたがいくつかの深刻なハッキングの楽しみのためにあなたがいるオプションでなければ。
他のヒント
リファクタリングの選択肢が本当にない場合は、スーパーメソッドのすべてをmock/stabeすることができます。
class BaseService {
public void validate(){
fail(" I must not be called");
}
public void save(){
//Save method of super will still be called.
validate();
}
}
class ChildService extends BaseService{
public void load(){}
public void save(){
super.save();
load();
}
}
@Test
public void testSave() {
ChildService classToTest = Mockito.spy(new ChildService());
// Prevent/stub logic in super.save()
Mockito.doNothing().when((BaseService)classToTest).validate();
// When
classToTest.save();
// Then
verify(classToTest).load();
}
ChildService.save()メソッドから異なる方法にコードをリファクトすることを検討し、childService.save()をテストする代わりにその新しい方法をテストすることを検討してください。
例:
class BaseService {
public void save() {...}
}
public Childservice extends BaseService {
public void save(){
newMethod();
super.save();
}
public void newMethod(){
//some codes
}
}
スーパークラスメソッドを呼び出すサブクラスで保護されたパッケージ(同じパッケージでテストクラスを想定)メソッドを作成し、オーバーライドされたサブクラスメソッドでそのメソッドを呼び出します。その後、スパイパターンを使用して、テストでこの方法に期待を設定できます。きれいではありませんが、テストのスーパーメソッドのすべての期待設定に対処しなければならないよりも確かに優れています
イウェインの応答に完全に同意しても(
継承よりも構成を好む
)、私は時々相続財産が自然に見えることがあり、私はそれを壊したりリファクタリングしたりすることはないと感じています。
だから、私の提案:
/**
* BaseService is now an asbtract class encapsulating
* some common logic callable by child implementations
*/
abstract class BaseService {
protected void commonSave() {
// Put your common work here
}
abstract void save();
}
public ChildService extends BaseService {
public void save() {
// Put your child specific work here
// ...
this.commonSave();
}
}
そして、ユニットテストで:
ChildService childSrv = Mockito.mock(ChildService.class, Mockito.CALLS_REAL_METHODS);
Mockito.doAnswer(new Answer<Void>() {
@Override
public Boolean answer(InvocationOnMock invocation)
throws Throwable {
// Put your mocked behavior of BaseService.commonSave() here
return null;
}
}).when(childSrv).commonSave();
childSrv.save();
Mockito.verify(childSrv, Mockito.times(1)).commonSave();
// Put any other assertions to check child specific work is done
その理由は、基本クラスが公開されていないため、モッキートは視認性のためにそれを傍受することができません。ベースクラスをパブリックとして、またはサブクラスの@Override(パブリック)で変更すると、モキトはそれを正しくock笑できます。
public class BaseService{
public boolean foo(){
return true;
}
}
public ChildService extends BaseService{
}
@Test
@Mock ChildService childService;
public void testSave() {
Mockito.when(childService.foo()).thenReturn(false);
// When
assertFalse(childService.foo());
}
継承が理にかなっている場合の最も簡単なオプションは、スーパーを呼び出すための新しい方法(パッケージプライベート??)を作成し、実際のインスタンスをスパイしてから、superfindall()メソッドをmockしたい方法でmock superfindall()メソッドをmockすることです。親クラス1。カバレッジと可視性の点では完璧な解決策ではありませんが、仕事をするはずであり、適用するのは簡単です。
public Childservice extends BaseService {
public void save(){
//some code
superSave();
}
void superSave(){
super.save();
}
}