2017-08-25 3 views
1

J'écris une classe de test unitaire (en utilisant testng) qui a mocké des variables membres (en utilisant Mockito) et en exécutant les tests en parallèle. J'ai d'abord mis en place le faux dans une méthode @BeforeClass, et dans chaque cas de test, je casse quelque chose en créant un Mockito.when pour chaque cas exceptionnel. Ce que je vois (sans surprise) est que ces tests ne sont pas indépendants; le Mockito.when dans un cas de test affecte les autres. J'ai remarqué que je pourrais être mis en place les mocks avant chaque test, et j'ai changé le @BeforeClass à @BeforeMethod. Je ne m'attendais toujours pas à ce que ça passe de manière cohérente, car les tests fonctionnent tous sur le même objet fantaisie partagé en même temps. Cependant, tous les tests ont commencé à passer régulièrement. Ma question est "pourquoi"? Est-ce que cela va finalement échouer? Peu importe ce que je fais (Thread.sleep, etc), je ne peux pas reproduire un échec.Test parallèle avec simulation partagée

L'utilisation de @BeforeMethod est-elle suffisante pour rendre ces tests indépendants? Si oui, quelqu'un peut-il expliquer pourquoi?

code exemple ci-dessous:

public class ExampleTest { 
    @Mock 
    private List<String> list; 

    @BeforeClass // Changing to @BeforeMethod works for some reason 
    public void setup() throws NoSuchComponentException, ADPRuntimeException { 
     MockitoAnnotations.initMocks(this); 
     Mockito.when(list.get(0)).thenReturn("normal"); 
    } 

    @Test 
    public void testNormalCase() throws InterruptedException { 
     assertEquals(list.get(0), "normal"); // Fails with expected [normal] but found [exceptional] 
    } 

    @Test 
    public void testExceptionalCase() throws InterruptedException { 
     Mockito.when(list.get(0)).thenReturn("exceptional"); 
     assertEquals(list.get(0), "exceptional"); 
    } 
} 

Répondre

4

Le problème ici est que TestNG crée une instance de votre classe de test ExampleTest ce qui est l'instance qui est utilisé par vos deux @Test méthodes. Donc, lorsque vous avez utilisé @BeforeClass, vous avez des échecs aléatoires avec testNormalCase() si testExceptionalCase() a été exécuté en premier et a modifié l'état de votre classe de test. Lorsque vous avez modifié votre annotation pour qu'elle soit @BeforeMethod, l'installation était exécutée juste avant l'exécution de la méthode @Test.

Ainsi, la configuration fixerait l'état pour testNormalCase() qui est la raison pour laquelle il passerait, et depuis testExceptionalCase() se fortifiait en interne en utilisant l'état Mockito.when() puis affirmations en cours d'exécution, il passerait tout le temps aussi bien.

Mais il y a un scénario dans lequel votre configuration sera toujours pas, à savoir., Lorsque vous utilisez parallel="methods" attribut dans votre balise <suite> au sein de votre fichier xml suite testng à savoir, lorsque vous configurez TestNG et instruisez pour exécuter toutes les méthodes @Test en parallèle.

Dans ce cas, le Mockito.when() au sein testExceptionalCase() auront une incidence sur l'état partagé [puisque vous utilisez this de manière partagée entre tous vos @Test méthodes] causant testNormalCase() au hasard à l'échec.

Pour résoudre ce problème, je vous suggère de faire ce qui suit:

  • Ne pas partager this entre vos méthodes @Test, mais maison séparément en dehors de votre classe de test soit, maison tous les membres de données votre classe de test dans un pojo séparé qui serait moqué plutôt que de se moquer this.
  • Utilisez un ThreadLocal pour stocker l'état qui est en train d'être raillé par Mockito.when(), puis exécutez des assertions sur le ThreadLocal à partir de vos méthodes .
+0

Je vois, 'parallel =" "ne faisait pas ce que je pensais faire. C'est logique, et merci pour les suggestions de contourner ce problème quand je change de méthodes 'parallel =" "'! – ChrisV