113

いくつかのテストでMockitoを使用しています。

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

class BaseService {  
    public void save() {...}  
}

public Childservice extends BaseService {  
    public void save(){  
        //some code  
        super.save();
    }  
}   

super.saveの2番目の呼び出し( )のみをモックしたいChildService。最初の呼び出しでは、実際のメソッドを呼び出す必要があります。それを行う方法はありますか?

4

10 に答える 10

99

リファクタリングの選択肢が本当にない場合は、スーパーメソッド呼び出しですべてをモック/スタブすることができます。

    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();
    }
于 2010-10-01T11:18:59.090 に答える
65

いいえ、Mockitoはこれをサポートしていません。

これはあなたが探している答えではないかもしれませんが、あなたが見ているのは設計原則を適用していないことの症状です:

継承よりも構成を優先する

スーパークラスを拡張する代わりに戦略を抽出すると、問題はなくなります。

ただし、コードを変更することは許可されていませんが、とにかくテストする必要がある場合は、この厄介な方法で、まだ希望があります。一部のAOPツール(AspectJなど)を使用すると、コードをスーパークラスメソッドに織り込み、その実行を完全に回避できます(うんざり)。プロキシを使用している場合、これは機能しません。バイトコードの変更(ロード時のウィービングまたはコンパイル時のウィービングのいずれか)を使用する必要があります。PowerMockやPowerMockitoのように、このタイプのトリックをサポートするモックフレームワークもあります。

リファクタリングに行くことをお勧めしますが、それが選択肢ではない場合は、深刻なハッキングを楽しむことができます。

于 2010-08-13T17:59:29.320 に答える
6

ChildService.save()メソッドから別のメソッドにコードをリファクタリングし、ChildService.save()をテストする代わりに、その新しいメソッドをテストすることを検討してください。これにより、スーパーメソッドへの不要な呼び出しを回避できます。

例:

class BaseService {  
    public void save() {...}  
}

public Childservice extends BaseService {  
    public void save(){  
        newMethod();    
        super.save();
    }
    public void newMethod(){
       //some codes
    }
} 
于 2013-05-17T21:27:11.663 に答える
3

PowerMockitoを使用してスーパークラスメソッドを抑制する方法を見つけました。これには3つの簡単なステップが必要です

  1. PowerMockito.suppressメソッドとMemberMatcher.methodsDeclaredInメソッドを使用して、親クラスメソッドを抑制します

  2. 次に、@PrepareForTestにParentクラスを追加します

  3. PowerMockを使用してテストクラスを実行します。つまり、テストクラスの上に@RunWith(PowerMockRunner.class)を追加します。

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({BaseService.class})
    public class TestChildService(){
    
        @Spy
        private ChildService testChildServiceObj = Mockito.spy(new ChildService());
    
        @Test
        public void testSave(){
            PowerMockito.suppress(MemberMatcher.methodsDeclaredIn(BaseService.class));
    
            //your further test code
    
            testChildServiceObj.save();
        }
    }
    

注:これは、スーパークラスメソッドが何も返さない場合にのみ機能します。

于 2021-03-11T14:27:28.033 に答える
2

継承が理にかなっている場合の最も簡単なオプションは、スーパーを呼び出す新しいメソッド(パッケージprivate ??)を作成し(superFindallと呼びましょう)、実際のインスタンスをスパイしてから、モックしたい方法でsuperFindAll()メソッドをモックすることです。親クラス1。カバレッジと可視性の点で完璧なソリューションではありませんが、それは仕事をするはずであり、適用するのは簡単です。

 public Childservice extends BaseService {
    public void save(){
        //some code
        superSave();
    }

    void superSave(){
        super.save();
    }
}
于 2017-05-10T19:59:44.337 に答える
1

スーパークラスメソッドを呼び出すサブクラスにパッケージ保護(同じパッケージ内のテストクラスを想定)メソッドを作成してから、オーバーライドされたサブクラスメソッドでそのメソッドを呼び出します。次に、スパイパターンを使用して、テストでこの方法に期待を設定できます。きれいではありませんが、テストでスーパーメソッドのすべての期待設定を処理するよりも確かに優れています

于 2012-05-22T22:04:12.910 に答える
1

私がiweinの応答に完全に同意したとしても(

継承よりも構成を優先する

)、継承が自然に見える場合があり、単体テストのためだけに継承を壊したりリファクタリングしたりすることはありません。

だから、私の提案:

/**
 * 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
于 2018-04-04T16:27:30.033 に答える
1

これはPowerMockitoを使用して行うことができ、親クラスのメソッドのみの動作を、子のクラスメソッドのテストを続行することで置き換えることができます。メソッドが何らかの値、たとえば文字列を返している場合でも、次のようなことができます。

@RunWith(PowerMockRunner.class)
@PrepareForTest({ BaseService.class })
public class TestChildService() {

    private BasicService basicServiceObj;
    private ChildService testee;

    @Before
    public void init() {
        testee = new ChildService();
        basicServiceObj = PowerMockito.spy(new BaseService());
        PowerMockito.doReturn("Result").when(basicServiceObj, "save", ... optionalArgs);
    }

    @Test
    public void testSave(){
        testee.save();
    }
}

何も返さない場合(void)は、代わりにdoReturnを使用できますdoNothingoptionalArgsメソッドに引数がある場合はいくつか追加し、ない場合はその部分をスキップします。

于 2021-05-13T09:59:01.363 に答える
0

その理由は、基本クラスが公開されていないため、Mockitoは可視性のためにそれをインターセプトできません。基本クラスを公開に変更するか、サブクラスの@Overrideを(公開として)変更すると、Mockitoはそれを正しくモックできます。

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());
}
于 2013-07-10T09:48:25.413 に答える
-1

ほとんどの場合に機能する単純なアプローチがあります。オブジェクトをスパイして、モックしたいメソッドをスタブすることができます。

次に例を示します。

MyClass myObjectSpy = Mockito.spy(myObject);
org.mockito.Mockito.doReturn("yourReturnValue").when(mySpyObject).methodToMock(any()..);

したがって、オブジェクトをテストするときはmyObjectSpyを使用でき、methodToMockが呼び出されると、モックメソッドによって通常の動作が上書きされます。

returnを使用するメソッドのこのコード。voidメソッドがある場合は、代わりにdoNothingを使用できます。

于 2021-05-10T23:14:42.417 に答える