web-dev-qa-db-ja.com

Mockito 2を使用して最終的なKotlinクラスをモックすることはできません

Mockito 2を使用してKotlin最終クラスを模擬できません。さらにRobolectricを使用しています。

これは私のテストコードです:

_@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
public class Test {

    // more mocks

    @Mock
    MyKotlinLoader kotlinLoader;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }
}
_

setUp()メソッドでモックを初期化しようとすると、テストは失敗します。

さらに、コードで次のGradle依存関係を使用しています。

_testCompile 'org.robolectric:robolectric:3.3.2'
testCompile 'org.robolectric:shadows-multidex:3.3.2'
testCompile 'org.robolectric:shadows-support-v4:3.3.2'
testCompile("org.powermock:powermock-api-mockito2:1.7.0") {
    exclude module: 'hamcrest-core'
    exclude module: 'objenesis'
}
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-inline:2.8.9'
_

他のすべての単体テストはこの構成を使用してパスしますが、Kotlinクラスをモックしようとするとすぐに、次のエラーがスローされます。

_Mockito cannot mock/spy because : - final class_

私はMockitoバージョン2を使用しており、inline依存関係を使用しているため、最終クラスを自動的にモックできるようになっています。

17
blackpanther

たとえば、Powermockを使用できます。

import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.spy;
import static org.powermock.api.mockito.PowerMockito.when;

@RunWith(RobolectricTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21)
@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "Android.*" })
@PrepareForTest({FinalClass1.class, FinalClass2.class})
public class Test {
    @Rule
    public PowerMockRule rule = new PowerMockRule();

    ... // your code here
}
1
DeKaNszn

PowerMockは独自のMockMakerを実装しているため、たとえPowerMockが依存関係として追加され、使用されていない場合でも、Mockito mock-maker-inlineとの互換性がなくなります。パスに2つのorg.mockito.plugins.MockMakerが存在する場合、どれか1つしか使用できません。

ただし、PowerMockは別のMockMakerへの呼び出しを委任することができ、その場合、テストはPowerMockなしで実行されます。 PowerMock 1.7.0以降、これはPowerMock構成を使用して構成できます。

MockMakerは、ファイルorg/powermock/extensions/configuration.propertiesを作成して次のように設定することで構成できます。

mockito.mock-maker-class=mock-maker-inline

PowerMockでMockito mock-maker-inlineを使用する例: https://github.com/powermock/powermock-examples-maven/tree/master/mockito2

12

Mockito 2.1.0以降、最終的な型、列挙型、最終的なメソッドをモックする可能性があります。元の質問へのコメントですでに言及されていました。

これを行うには、フォルダー(存在しない場合)test/resources/mockito-extensionsを作成し、org.mockito.plugins.MockMakerという名前のファイルを追加して、次の行を追加する必要があります。

mock-maker-inline

enter image description here

documentation および tutorial へのリンク

4

以下の依存関係をbuild.gradleに追加してみてください。

testImplementation 'org.mockito:mockito-inline:2.8.47'

2.8.47の代わりにmockitoバージョンに置き換えます。これは、問題にpowermockを使用しないようにするのに役立ちます。

以下のリンクを見て、このことがどのように機能するかを確認してください。

mockitoで最終クラスをモックする方法

1
rafa

mock-maker-inlineは、他の回答で指摘されているように機能します。しかし、それは非常に遅いです。 all-openこの問題を回避するためのプラグイン。

これを行うには、次のものが必要です。

  1. 注釈を作成します。
annotation class Mockable
  1. build.gradleファイル:
dependencies {
  classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
}

apply plugin: 'kotlin-allopen'

allOpen {
  annotation('com.example.Mockable')
}
  1. モックしたいクラスに注釈を付けます。
@Mockable
class Foo {
  fun calculateTheFoo(): Int {
    sleep(1_000) // Difficult things here
    return 1
  }
}

さらに詳しい情報が必要な場合は、私のブログ記事を読んで詳細を説明することができます。 Mockitoを使用したKotlinクラスのモックアップ—高速な方法

1
Brais Gabin

Kotlinは、デコレータパターンをシンプルかつ簡潔に実装します。

_open class OpenClass() : SomeInterface by FinalClass()
_

これにより、実際には、各SomeInterfaceメンバーが、openラッパークラスのFinalClass()インスタンスへのラップされた呼び出しでオーバーライドされます。次に、このラッパーをテストに挿入できます。

ターゲットクラスがライブラリに存在するプロジェクトでは、これに頼らなければならず、_all-open_コンパイラプラグインでは変更できないように見えました。

0
Jarrod Moldrich

実装ではなく、インターフェースにプログラムしてみましょう。インターフェイスを抽出し、コードで使用して、モックすることができます。たとえば、次は機能しません。

import com.nhaarman.mockito_kotlin.mock
class MyFinalClass {...}
(snip)
private val MyFinalClass = mock()

それではインターフェースを抽出しましょう:

class MyFinalClass : MyInterface {...}
(snip)
private val MyInterface = mock()
0
Dol.Gopil

Kotlinでは、すべてのクラスがデフォルトでfinalであるためです。

クラス宣言にopenを追加することも検討する必要があります。

例:open class MyClasss{}

0