web-dev-qa-db-fra.com

Impossible de se moquer de la classe finale de Kotlin avec Mockito 2

Je ne peux pas me moquer d'une dernière classe de Kotlin avec Mockito 2. J'utilise Robolectric en plus.

Ceci est mon code de test:

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

    // more mocks

    @Mock
    MyKotlinLoader kotlinLoader;

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

Le test échoue lorsque nous essayons d'initialiser les simulacres dans la méthode setUp().

De plus, j'utilise les dépendances de gradle suivantes dans mon code:

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'

Tous les autres tests unitaires réussissent avec cette configuration, mais dès que j'essaie de me moquer de la classe Kotlin, l'erreur suivante est générée:

Mockito cannot mock/spy because : - final class

Veuillez noter que j'utilise Mockito version 2 et que j'utilise la dépendance inline qui permet automatiquement de simuler les classes finales.

11
blackpanther

Vous pouvez utiliser Powermock pour cela, par exemple:

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 implémente sa propre MockMaker, ce qui entraîne une incompatibilité avec Mockito mock-maker-inline, même si PowerMock est simplement ajouté en tant que dépendance et n'est pas utilisé. Si deux org.mockito.plugins.MockMaker existent dans le chemin, alors un seul peut être utilisé, lequel est indéterminé.

PowerMock peut toutefois déléguer des appels à un autre MockMaker, et les tests sont alors exécutés sans PowerMock. Depuis PowerMock 1.7.0, cela peut être configuré avec la configuration de PowerMock.

Le MockMaker peut être configuré en créant le fichier org/powermock/extensions/configuration.properties et en définissant:

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

Exemple d'utilisation de Mockito mock-maker-inline avec PowerMock: https://github.com/powermock/powermock-examples-maven/tree/master/mockito2

12
Arthur Zagretdinov

Kotlin rend le motif de décorateur mort simple et concis à mettre en œuvre:

open class OpenClass() : SomeInterface by FinalClass()

Cela remplacera en effet chacun des membres SomeInterface par des appels encapsulés à l'instance FinalClass() dans votre classe d'encapsulation open. Vous pouvez ensuite injecter cette enveloppe dans vos tests.

J'ai dû recourir à cela sur un projet dans lequel la classe cible vivait dans une bibliothèque et semblait intouchable grâce au plugin de compilation all-open.

0
Jarrod Moldrich

Laissez-nous programmer aux interfaces, pas aux implémentations. Vous pouvez extraire une interface, l'utiliser dans votre code et la simuler. Par exemple, ce qui suit ne fonctionnera pas:

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

Alors, extrayons une interface:

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

Car dans kotlin, toutes les classes sont finales par défaut. 

Vous devez également envisager d'ajouter open à la déclaration de classe. 

Exemple: open class MyClasss{}

0