web-dev-qa-db-ja.com

連鎖呼び出しのモックまたはスタブ

protected int parseExpire(CacheContext ctx) throws AttributeDefineException {
    Method targetMethod = ctx.getTargetMethod();
    CacheEnable cacheEnable = targetMethod.getAnnotation(CacheEnable.class);
    ExpireExpr cacheExpire = targetMethod.getAnnotation(ExpireExpr.class);
    // check for duplicate setting
    if (cacheEnable.expire() != CacheAttribute.DO_NOT_EXPIRE && cacheExpire != null) {
        throw new AttributeDefineException("expire are defined both in @CacheEnable and @ExpireExpr");
    }
    // expire time defined in @CacheEnable or @ExpireExpr
    return cacheEnable.expire() != CacheAttribute.DO_NOT_EXPIRE ? cacheEnable.expire() : parseExpireExpr(cacheExpire, ctx.getArgument());
}

それがテストする方法です、

Method targetMethod = ctx.getTargetMethod();
CacheEnable cacheEnable = targetMethod.getAnnotation(CacheEnable.class);

3つのCacheContext、Method、CacheEnableをモックする必要があります。テストケースをもっと簡単にするアイデアはありますか?

57
jilen

Mockito 連鎖スタブを処理できます

Foo mock = mock(Foo.class, RETURNS_DEEP_STUBS);

// note that we're stubbing a chain of methods here: getBar().getName()
when(mock.getBar().getName()).thenReturn("deep");

// note that we're chaining method calls: getBar().getName()
assertEquals("deep", mock.getBar().getName());

知る限り、チェーンの最初のメソッドはモックを返します。これは、2番目のチェーンメソッド呼び出しで値を返すように設定されています。

Mockitoの著者は、これはレガシーコードにのみ使用する必要があることに注意しています。そうでない場合は、動作をCacheContextにプッシュし、ジョブ自体を実行するために必要な情報を提供することをお勧めします。 CacheContextから取得する情報量は、クラスに feature envy があることを示唆しています。

127
Lunivore

テストケースを簡単にするための私の提案は、メソッドをリファクタリングすることです。

メソッドのテストで問題が発生したときはいつでもコードの匂いがするので、テストが難しいのはなぜですか。また、コードのテストが難しい場合は、おそらく使用と保守が難しいでしょう。

この場合は、いくつかのレベルに及ぶメソッドチェーンがあるためです。おそらく、ctx、cacheEnable、およびcacheExpireをパラメーターとして渡します。

3
Doug R

あなたがKotlinを使用している場合に備えて。 MockKは、チェーンが悪い習慣であることについては何も言わず、簡単に this を実行できるようにします。

val car = mockk<Car>()

every { car.door(DoorType.FRONT_LEFT).windowState() } returns WindowState.UP

car.door(DoorType.FRONT_LEFT) // returns chained mock for Door
car.door(DoorType.FRONT_LEFT).windowState() // returns WindowState.UP

verify { car.door(DoorType.FRONT_LEFT).windowState() }

confirmVerified(car)
2
yuranos87

JMockitは、完全に切り替えられたansを使いやすいことがわかりました。それを使用したテストケースを参照してください。

https://github.com/ko5tik/andject/blob/master/src/test/Java/de/pribluda/Android/andject/ViewInjectionTest.Java

ここでは、Android SKDから派生し、完全にスタブ化されたActivity基本クラスをモックアップします。

テストケースでは次のようになります。

public void testFoo(@Mocked final Method targetMethod, 
                    @Mocked  final CacheContext context,
                    @Mocked final  CacheExpire ce) {
    new Expectations() {
       {
           // specify expected sequence of infocations here

           context.getTargetMethod(); returns(method);
       }
    };

    // call your method
    assertSomething(objectUndertest.cacheExpire(context))
2