{"id":350,"date":"2024-04-20T20:24:08","date_gmt":"2024-04-20T19:24:08","guid":{"rendered":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=350"},"modified":"2024-06-01T20:29:35","modified_gmt":"2024-06-01T19:29:35","slug":"asynchrone-jobs-in-java-unit-tests-ohne-thread-sleep-testen","status":"publish","type":"post","link":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/?p=350","title":{"rendered":"Asynchrone Jobs in Java-Unit-Tests ohne Thread.sleep() testen"},"content":{"rendered":"\n<p>Das Testen von asynchronen Jobs in Java kann eine Herausforderung darstellen, insbesondere wenn diese Jobs \u00fcber einen <code>ExecutorService<\/code> ausgef\u00fchrt werden. In diesem Artikel werden wir uns darauf konzentrieren, wie man solche Jobs testen kann, ohne <code>Thread.sleep()<\/code> mit einer fixen Wartezeit zu verwenden. Wir werden auch die Probleme beleuchten, die <code>Thread.sleep()<\/code> mit sich bringt und warum andere L\u00f6sungen vorzuziehen sind.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Probleme mit <code>Thread.sleep()<\/code><\/h2>\n\n\n\n<p><code>Thread.sleep()<\/code> wird oft in Tests verwendet, um eine bestimmte Zeitspanne zu warten, bis ein asynchroner Job abgeschlossen ist. Dies hat jedoch mehrere Nachteile:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>Unzuverl\u00e4ssigkeit<\/strong>: Die Dauer, die ein asynchroner Job ben\u00f6tigt, um abgeschlossen zu werden, kann variieren, abh\u00e4ngig von der Systemlast und anderen Faktoren. Eine fixe Wartezeit kann entweder zu kurz sein (und der Test schl\u00e4gt fehl) oder zu lang (und der Test dauert unn\u00f6tig lange).<\/li>\n\n\n\n<li><strong>Effizienz<\/strong>: L\u00e4ngere Wartezeiten machen Tests langsam, was die Entwicklungszeit erh\u00f6ht und die Effizienz mindert.<\/li>\n\n\n\n<li><strong>Ressourcenverbrauch<\/strong>: <code>Thread.sleep()<\/code> blockiert den Test-Thread und verbraucht somit Ressourcen unn\u00f6tig.<\/li>\n\n\n\n<li><strong>Fehlende Synchronisation<\/strong>: <code>Thread.sleep()<\/code> synchronisiert den Test nicht wirklich mit dem Ende des asynchronen Jobs, sondern wartet lediglich eine vordefinierte Zeit ab. Das Ergebnis kann somit inkonsistent sein.<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\">Bessere Ans\u00e4tze zum Testen von asynchronen Jobs<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. Nutzung von <code>Future<\/code> und <code>ExecutorService<\/code><\/h3>\n\n\n\n<p>Eine g\u00e4ngige Methode ist die Nutzung von <code>Future<\/code>-Objekten, die von einem <code>ExecutorService<\/code> zur\u00fcckgegeben werden. Ein <code>Future<\/code> erm\u00f6glicht es, auf das Ergebnis eines asynchronen Jobs zu warten.<\/p>\n\n\n\n<p>Beispiel:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> java.util.concurrent.*;\n\npublic <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AsyncJobTest<\/span> <\/span>{\n\n    private ExecutorService executorService;\n\n    @Before\n    public <span class=\"hljs-keyword\">void<\/span> setUp() {\n        executorService = Executors.newSingleThreadExecutor();\n    }\n\n    @After\n    public <span class=\"hljs-keyword\">void<\/span> tearDown() {\n        executorService.shutdown();\n    }\n\n    @Test\n    public <span class=\"hljs-keyword\">void<\/span> testAsyncJob() throws ExecutionException, InterruptedException {\n        Callable&lt;<span class=\"hljs-built_in\">String<\/span>&gt; job = () -&gt; {\n            <span class=\"hljs-comment\">\/\/ Simulierte lange Operation<\/span>\n            Thread.sleep(<span class=\"hljs-number\">1000<\/span>);\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"Job Result\"<\/span>;\n        };\n\n        Future&lt;<span class=\"hljs-built_in\">String<\/span>&gt; future = executorService.submit(job);\n\n        <span class=\"hljs-comment\">\/\/ Warten auf das Ergebnis<\/span>\n        <span class=\"hljs-built_in\">String<\/span> result = future.get();  <span class=\"hljs-comment\">\/\/ Dies blockiert, bis der Job abgeschlossen ist<\/span>\n\n        assertEquals(<span class=\"hljs-string\">\"Job Result\"<\/span>, result);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">2. Nutzung von <code>CountDownLatch<\/code><\/h3>\n\n\n\n<p><code>CountDownLatch<\/code> ist eine Synchronisationshilfe, die verwendet werden kann, um einen oder mehrere Threads warten zu lassen, bis eine Reihe von Operationen abgeschlossen sind.<\/p>\n\n\n\n<p>Beispiel:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> java.util.concurrent.*;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-keyword\">static<\/span> org.junit.Assert.*;\n<span class=\"hljs-keyword\">import<\/span> org.junit.*;\n\npublic <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AsyncJobTest<\/span> <\/span>{\n\n    private ExecutorService executorService;\n\n    @Before\n    public <span class=\"hljs-keyword\">void<\/span> setUp() {\n        executorService = Executors.newSingleThreadExecutor();\n    }\n\n    @After\n    public <span class=\"hljs-keyword\">void<\/span> tearDown() {\n        executorService.shutdown();\n    }\n\n    @Test\n    public <span class=\"hljs-keyword\">void<\/span> testAsyncJobWithCountDownLatch() throws InterruptedException {\n        CountDownLatch latch = <span class=\"hljs-keyword\">new<\/span> CountDownLatch(<span class=\"hljs-number\">1<\/span>);\n        AtomicReference&lt;<span class=\"hljs-built_in\">String<\/span>&gt; result = <span class=\"hljs-keyword\">new<\/span> AtomicReference&lt;&gt;();\n\n        Runnable job = () -&gt; {\n            <span class=\"hljs-keyword\">try<\/span> {\n                <span class=\"hljs-comment\">\/\/ Simulierte lange Operation<\/span>\n                Thread.sleep(<span class=\"hljs-number\">1000<\/span>);\n                result.set(<span class=\"hljs-string\">\"Job Result\"<\/span>);\n            } <span class=\"hljs-keyword\">catch<\/span> (InterruptedException e) {\n                Thread.currentThread().interrupt();\n            } <span class=\"hljs-keyword\">finally<\/span> {\n                latch.countDown();\n            }\n        };\n\n        executorService.submit(job);\n\n        <span class=\"hljs-comment\">\/\/ Warten bis der Latch auf null gez\u00e4hlt wurde<\/span>\n        latch.await();  \n\n        assertEquals(<span class=\"hljs-string\">\"Job Result\"<\/span>, result.get());\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">3. Nutzung von <code>CompletableFuture<\/code><\/h3>\n\n\n\n<p><code>CompletableFuture<\/code> ist eine erweiterte Implementierung des <code>Future<\/code>-Interfaces und bietet viele M\u00f6glichkeiten, asynchrone Operationen effizient zu handhaben.<\/p>\n\n\n\n<p>Beispiel:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> java.util.concurrent.*;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-keyword\">static<\/span> org.junit.Assert.*;\n<span class=\"hljs-keyword\">import<\/span> org.junit.*;\n\npublic <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AsyncJobTest<\/span> <\/span>{\n\n    @Test\n    public <span class=\"hljs-keyword\">void<\/span> testAsyncJobWithCompletableFuture() throws ExecutionException, InterruptedException {\n        CompletableFuture&lt;<span class=\"hljs-built_in\">String<\/span>&gt; future = CompletableFuture.supplyAsync(() -&gt; {\n            <span class=\"hljs-keyword\">try<\/span> {\n                <span class=\"hljs-comment\">\/\/ Simulierte lange Operation<\/span>\n                Thread.sleep(<span class=\"hljs-number\">1000<\/span>);\n            } <span class=\"hljs-keyword\">catch<\/span> (InterruptedException e) {\n                Thread.currentThread().interrupt();\n            }\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">\"Job Result\"<\/span>;\n        });\n\n        <span class=\"hljs-comment\">\/\/ Warten auf das Ergebnis<\/span>\n        <span class=\"hljs-built_in\">String<\/span> result = future.get();  \n\n        assertEquals(<span class=\"hljs-string\">\"Job Result\"<\/span>, result);\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">4. Nutzung von Mockito und ArgumentCaptor<\/h3>\n\n\n\n<p>Wenn es darum geht, zu pr\u00fcfen, ob ein asynchroner Job korrekt aufgerufen wird, kann Mockito zusammen mit <code>ArgumentCaptor<\/code> verwendet werden.<\/p>\n\n\n\n<p>Beispiel:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-keyword\">static<\/span> org.mockito.Mockito.*;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-keyword\">static<\/span> org.junit.Assert.*;\n<span class=\"hljs-keyword\">import<\/span> org.junit.*;\n<span class=\"hljs-keyword\">import<\/span> org.mockito.ArgumentCaptor;\n\n<span class=\"hljs-keyword\">import<\/span> java.util.concurrent.ExecutorService;\n\npublic <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AsyncJobTest<\/span> <\/span>{\n\n    private ExecutorService executorService;\n    private MyService myService;\n\n    @Before\n    public <span class=\"hljs-keyword\">void<\/span> setUp() {\n        executorService = mock(ExecutorService.class);\n        myService = <span class=\"hljs-keyword\">new<\/span> MyService(executorService);\n    }\n\n    @Test\n    public <span class=\"hljs-keyword\">void<\/span> testAsyncJobWithMockito() {\n        ArgumentCaptor&lt;Runnable&gt; captor = ArgumentCaptor.forClass(Runnable.class);\n\n        myService.runAsyncJob();\n\n        verify(executorService).submit(captor.capture());\n        Runnable capturedRunnable = captor.getValue();\n\n        <span class=\"hljs-comment\">\/\/ Simuliere die Ausf\u00fchrung des Jobs<\/span>\n        capturedRunnable.run();\n\n        assertEquals(<span class=\"hljs-string\">\"Job Result\"<\/span>, myService.getResult());\n    }\n\n    <span class=\"hljs-comment\">\/\/ Beispielservice, der einen asynchronen Job ausf\u00fchrt<\/span>\n    <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyService<\/span> <\/span>{\n        private final ExecutorService executorService;\n        private <span class=\"hljs-built_in\">String<\/span> result;\n\n        MyService(ExecutorService executorService) {\n            <span class=\"hljs-keyword\">this<\/span>.executorService = executorService;\n        }\n\n        <span class=\"hljs-keyword\">void<\/span> runAsyncJob() {\n            executorService.submit(() -&gt; {\n                <span class=\"hljs-comment\">\/\/ Simulierte lange Operation<\/span>\n                <span class=\"hljs-keyword\">try<\/span> {\n                    Thread.sleep(<span class=\"hljs-number\">1000<\/span>);\n                } <span class=\"hljs-keyword\">catch<\/span> (InterruptedException e) {\n                    Thread.currentThread().interrupt();\n                }\n                result = <span class=\"hljs-string\">\"Job Result\"<\/span>;\n            });\n        }\n\n        <span class=\"hljs-built_in\">String<\/span> getResult() {\n            <span class=\"hljs-keyword\">return<\/span> result;\n        }\n    }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code-Sprache:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Fazit<\/h2>\n\n\n\n<p>Das Testen von asynchronen Jobs ohne <code>Thread.sleep()<\/code> bietet zahlreiche Vorteile. Es erh\u00f6ht die Zuverl\u00e4ssigkeit und Effizienz der Tests und verringert den Ressourcenverbrauch. Durch die Nutzung von <code>Future<\/code>, <code>CountDownLatch<\/code>, <code>CompletableFuture<\/code> oder Mockito kann man sicherstellen, dass die Tests pr\u00e4zise und schnell sind. Jeder dieser Ans\u00e4tze hat seine eigenen Vorteile und kann je nach spezifischen Anforderungen des Tests verwendet werden. Die Wahl des richtigen Ansatzes h\u00e4ngt von den spezifischen Anforderungen und dem Kontext der Anwendung ab.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Das Testen von asynchronen Jobs in Java kann eine Herausforderung darstellen, insbesondere wenn diese Jobs \u00fcber einen ExecutorService ausgef\u00fchrt werden. In diesem Artikel werden wir uns darauf konzentrieren, wie man solche Jobs testen kann, ohne Thread.sleep() mit einer fixen Wartezeit zu verwenden. Wir werden auch die Probleme beleuchten, die Thread.sleep() mit sich bringt und warum [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-350","post","type-post","status-publish","format-standard","hentry","category-plain_java"],"_links":{"self":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/350","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=350"}],"version-history":[{"count":1,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/350\/revisions"}],"predecessor-version":[{"id":351,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=\/wp\/v2\/posts\/350\/revisions\/351"}],"wp:attachment":[{"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.xn--javaeinfacherklrt-4qb.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}