web-dev-qa-db-fra.com

Comment tester à l'unité un extrait de code exécuté dans le service d'exécution, en attente de Thread.sleep (heure)

Comment tester un code qui tourne dans le service exécuteur? Dans ma situation,

public void test() {
    Runnable R = new Runnable() {
        @Override
        public void run() {
            executeTask1();
            executeTask2();
        }
    };

    ExecutorService executorService = Executors.newSingleThreadExecutor();
    executorService.submit(R);
}

Lorsque je suis en train de tester des unités, je voudrais faire quelques validations que cette méthode exécute.

J'exécute ceci dans un service exécuteur, car il effectue certaines opérations sur le réseau.

Dans mes tests unitaires, je devais attendre que cette méthode termine son exécution. Existe-t-il un meilleur moyen de le faire au lieu d’attendre Thread.sleep(500).

Extrait de code de test unitaire:

@Test
public void testingTask() {
    mTestObject.test();
    final long threadSleepTime = 10000L;
    Thread.sleep(threadSleepTime);
    verify(abc, times(2))
            .acquireClient(a, b, c);
    verify(abd, times(1)).addCallback(callback);
}

Remarque: Je passe un objet de service exécuteur dans cette classe de constructeur. J'aimerais savoir s'il existe un bon moyen de tester au lieu d'attendre le temps de sommeil.

5
samuel koduri

Vous pouvez également implémenter vous-même un ExecutorService qui exécutera la tâche dans le même thread. Par exemple:

public class CurrentThreadExecutor implements Executor {
    public void execute(Runnable r) {
        r.run();
    }
}

Et ensuite, vous pouvez hériter de AbstractExecutorService et utiliser cette implémentation.

Si vous utilisez Guava, vous pouvez également utiliser MoreExecutors.newDirectExecutorService () car cela fait la même chose sans que vous n’ayez à en créer un vous-même.

6
claudio

Quelques options:

  • Extrayez le code hors du service de l'exécuteur et testez-le 'autonome', c'est-à-dire dans votre exemple, testez executeTask1() et executeTask2() seuls ou même ensemble, mais pas en les exécutant dans un thread séparé. Le fait que vous "passez un objet de service exécuteur dans cette classe de constructeur" est utile ici, car vous pourriez avoir 

    • Un test qui se moque du service de l'exécuteur et vérifie que vous lui soumettez l'exécutable correct 
    • Test (s) vérifiant le comportement de executeTask1() et executeTask2() sans les exécuter dans un thread séparé. 
  • Utilisez une CountDownLatch pour permettre à votre service de code-en-exécutant d'indiquer au thread de test qu'il est terminé. Par exemple:

    // this will be initialised and passed into the task which is run by the ExecutorService 
    // and will be decremented by that task on completion
    private CountDownLatch countdownLatch; 
    
    @Test(timeout = 1000) // just in case the latch is never decremented
    public void aTest() {
        // run your test
    
        // wait for completion
        countdownLatch.await();
    
        // assert 
        // ...
    }
    
  • Acceptez le fait que vous devez attendre que l’autre thread se termine et cachez la laideur des appels Thread.sleep dans vos scénarios de test en utilisant Awaitility . Par exemple:

    @Test
    public void aTest() {
        // run your test
    
        // wait for completion
        await().atMost(1, SECONDS).until(taskHasCompleted());
    
        // assert 
        // ...
    }
    
    private Callable<Boolean> taskHasCompleted() {
        return new Callable<Boolean>() {
            public Boolean call() throws Exception {
                // return true if your condition has been met
                return ...;
            }
        };
    }
    
1
glytching

Google Guava fournit une excellente classe appelée MoreExecutors qui m'a aidé lors du test de code s'exécutant dans des threads parallèles via Executor ou ExecutorService dans JUnit. Il vous permet de créer des instances Executor qui exécutent tout dans le même thread, essentiellement comme une maquette d'une vraie Executor. Le problème est que lorsque JUnit n'est pas au courant, les éléments sont exécutés dans d'autres threads. Par conséquent, ces Executors de MoreExecutors facilitent grandement le test, car ce n'est pas en fait parallèle dans un autre thread.

Voir la documentation MoreExecutorshttps://google.github.io/guava/releases/19.0/api/docs/com/google/common/util/concurrent/MoreExecutors.html

Vous pouvez modifier votre constructeur de classe ou ajouter un nouveau constructeur que vous utilisez uniquement dans les tests, ce qui vous permet de fournir votre propre Executor ou ExecutorService. Puis passez celui de MoreExecutors.

Donc, dans le fichier de test, vous créeriez l’exécuteur factice en utilisant MoreExecutors

ExecutorService mockExecutor = MoreExecutors.newDirectExecutorService();

// or if you're using Executor instead of ExecutorService you can do MoreExecutors.newDirectExecutor()

MyService myService = new MyService(mockExecutor);

Ensuite, dans votre classe, vous ne créez un véritable exécuteur que s'il n'a pas été fourni dans le constructeur.

public MyService() {}
    ExecutorService threadPool;

    public MyService(ExecutorService threadPool) {
        this.threadPool = threadPool;
    }

    public void someMethodToTest() {
        if (this.threadPool == null) {
            // if you didn't provide the executor via constructor in the unit test, 
            // it will create a real one
            threadPool = Executors.newFixedThreadPool(3);
        }
        threadPool.execute(...etc etc)
        threadPool.shutdown()
    }
}
0
ejfrancis

Vous pouvez utiliser l'instance Future renvoyée par executorService.submit (R).

De la documentation:

https://docs.Oracle.com/javase/7/docs/api/Java/util/concurrent/ExecutorService.html#submit(Java.lang.Runnable)

Soumet une tâche exécutable pour exécution et renvoie un futur représentant cette tâche. La méthode get de Future retournera la valeur null si elle aboutit.

0
mrtexaz