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.
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
}
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
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
.
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()
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{}