How to unit test coroutine when it contains coroutine delay?










1














When I add a coroutine delay() in my view model, the remaining part of the code will not be executed.



This is my demo code:



class SimpleViewModel : ViewModel(), CoroutineScope 

override val coroutineContext: CoroutineContext
get() = Dispatchers.Unconfined

var data = 0

fun doSomething()
launch
delay(1000)
data = 1




class ScopedViewModelTest

@Test
fun coroutineDelay()
// Arrange
val viewModel = SimpleViewModel()

// ActTes
viewModel.doSomething()

// Assert
Assert.assertEquals(1, viewModel.data)




I got the assertion result:



java.lang.AssertionError: 
Expected :1
Actual :0


Any idea how to fix this?










share|improve this question




























    1














    When I add a coroutine delay() in my view model, the remaining part of the code will not be executed.



    This is my demo code:



    class SimpleViewModel : ViewModel(), CoroutineScope 

    override val coroutineContext: CoroutineContext
    get() = Dispatchers.Unconfined

    var data = 0

    fun doSomething()
    launch
    delay(1000)
    data = 1




    class ScopedViewModelTest

    @Test
    fun coroutineDelay()
    // Arrange
    val viewModel = SimpleViewModel()

    // ActTes
    viewModel.doSomething()

    // Assert
    Assert.assertEquals(1, viewModel.data)




    I got the assertion result:



    java.lang.AssertionError: 
    Expected :1
    Actual :0


    Any idea how to fix this?










    share|improve this question


























      1












      1








      1







      When I add a coroutine delay() in my view model, the remaining part of the code will not be executed.



      This is my demo code:



      class SimpleViewModel : ViewModel(), CoroutineScope 

      override val coroutineContext: CoroutineContext
      get() = Dispatchers.Unconfined

      var data = 0

      fun doSomething()
      launch
      delay(1000)
      data = 1




      class ScopedViewModelTest

      @Test
      fun coroutineDelay()
      // Arrange
      val viewModel = SimpleViewModel()

      // ActTes
      viewModel.doSomething()

      // Assert
      Assert.assertEquals(1, viewModel.data)




      I got the assertion result:



      java.lang.AssertionError: 
      Expected :1
      Actual :0


      Any idea how to fix this?










      share|improve this question















      When I add a coroutine delay() in my view model, the remaining part of the code will not be executed.



      This is my demo code:



      class SimpleViewModel : ViewModel(), CoroutineScope 

      override val coroutineContext: CoroutineContext
      get() = Dispatchers.Unconfined

      var data = 0

      fun doSomething()
      launch
      delay(1000)
      data = 1




      class ScopedViewModelTest

      @Test
      fun coroutineDelay()
      // Arrange
      val viewModel = SimpleViewModel()

      // ActTes
      viewModel.doSomething()

      // Assert
      Assert.assertEquals(1, viewModel.data)




      I got the assertion result:



      java.lang.AssertionError: 
      Expected :1
      Actual :0


      Any idea how to fix this?







      android unit-testing kotlin coroutine kotlinx.coroutines






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 13 '18 at 0:02

























      asked Nov 12 '18 at 23:34









      Cody

      2,47822829




      2,47822829






















          2 Answers
          2






          active

          oldest

          votes


















          1














          You start a coroutine which suspends for 1 second before setting data to 1. Your test just invokes doSomething but does not wait until data is actually being set. If you add another, longer delay, to the test it will, work:



          @Test 
          fun coroutineDelay() = runBlocking
          ...
          viewModel.doSomething()
          delay(1100)
          ...



          You can also make the coroutine return a Deferred which you can wait on:



          fun doSomething(): Deferred<Unit> 
          return async
          delay(1000)
          data = 1




          With await there's no need to delay your code anymore:



          val model = SimpleViewModel()
          model.doSomething().await()





          share|improve this answer






















          • Is there a way to not including the specific delay number in the test?
            – Cody
            Nov 13 '18 at 0:20







          • 1




            with async for example
            – s1m0nw1
            Nov 13 '18 at 0:36


















          2














          The first issue in your code is that SimpleViewModel.coroutineContext has no Job associated with it. The whole point of making your view model a CoroutineScope is the ability to centralize the cancelling of all coroutines it starts. So add the job as follows (note the absence of a custom getter):



          class SimpleViewModel : ViewModel(), CoroutineScope 

          override val coroutineContext = Job() + Dispatchers.Unconfined

          var data = 0

          fun doSomething()
          launch
          delay(1000)
          data = 1





          Now your test code can ensure it proceeds to the assertions only after all the jobs your view model launched are done:



          class ScopedViewModelTest 

          @Test
          fun coroutineDelay()
          // Arrange
          val viewModel = SimpleViewModel()

          // ActTes
          viewModel.doSomething()

          // Assert
          runBlocking
          viewModel.coroutineContext[Job]!!.children.forEach it.join()

          Assert.assertEquals(1, viewModel.data)







          share|improve this answer




















            Your Answer






            StackExchange.ifUsing("editor", function ()
            StackExchange.using("externalEditor", function ()
            StackExchange.using("snippets", function ()
            StackExchange.snippets.init();
            );
            );
            , "code-snippets");

            StackExchange.ready(function()
            var channelOptions =
            tags: "".split(" "),
            id: "1"
            ;
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function()
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled)
            StackExchange.using("snippets", function()
            createEditor();
            );

            else
            createEditor();

            );

            function createEditor()
            StackExchange.prepareEditor(
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader:
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            ,
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            );



            );













            draft saved

            draft discarded


















            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53271646%2fhow-to-unit-test-coroutine-when-it-contains-coroutine-delay%23new-answer', 'question_page');

            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            1














            You start a coroutine which suspends for 1 second before setting data to 1. Your test just invokes doSomething but does not wait until data is actually being set. If you add another, longer delay, to the test it will, work:



            @Test 
            fun coroutineDelay() = runBlocking
            ...
            viewModel.doSomething()
            delay(1100)
            ...



            You can also make the coroutine return a Deferred which you can wait on:



            fun doSomething(): Deferred<Unit> 
            return async
            delay(1000)
            data = 1




            With await there's no need to delay your code anymore:



            val model = SimpleViewModel()
            model.doSomething().await()





            share|improve this answer






















            • Is there a way to not including the specific delay number in the test?
              – Cody
              Nov 13 '18 at 0:20







            • 1




              with async for example
              – s1m0nw1
              Nov 13 '18 at 0:36















            1














            You start a coroutine which suspends for 1 second before setting data to 1. Your test just invokes doSomething but does not wait until data is actually being set. If you add another, longer delay, to the test it will, work:



            @Test 
            fun coroutineDelay() = runBlocking
            ...
            viewModel.doSomething()
            delay(1100)
            ...



            You can also make the coroutine return a Deferred which you can wait on:



            fun doSomething(): Deferred<Unit> 
            return async
            delay(1000)
            data = 1




            With await there's no need to delay your code anymore:



            val model = SimpleViewModel()
            model.doSomething().await()





            share|improve this answer






















            • Is there a way to not including the specific delay number in the test?
              – Cody
              Nov 13 '18 at 0:20







            • 1




              with async for example
              – s1m0nw1
              Nov 13 '18 at 0:36













            1












            1








            1






            You start a coroutine which suspends for 1 second before setting data to 1. Your test just invokes doSomething but does not wait until data is actually being set. If you add another, longer delay, to the test it will, work:



            @Test 
            fun coroutineDelay() = runBlocking
            ...
            viewModel.doSomething()
            delay(1100)
            ...



            You can also make the coroutine return a Deferred which you can wait on:



            fun doSomething(): Deferred<Unit> 
            return async
            delay(1000)
            data = 1




            With await there's no need to delay your code anymore:



            val model = SimpleViewModel()
            model.doSomething().await()





            share|improve this answer














            You start a coroutine which suspends for 1 second before setting data to 1. Your test just invokes doSomething but does not wait until data is actually being set. If you add another, longer delay, to the test it will, work:



            @Test 
            fun coroutineDelay() = runBlocking
            ...
            viewModel.doSomething()
            delay(1100)
            ...



            You can also make the coroutine return a Deferred which you can wait on:



            fun doSomething(): Deferred<Unit> 
            return async
            delay(1000)
            data = 1




            With await there's no need to delay your code anymore:



            val model = SimpleViewModel()
            model.doSomething().await()






            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 13 '18 at 0:36

























            answered Nov 13 '18 at 0:08









            s1m0nw1

            25.3k641101




            25.3k641101











            • Is there a way to not including the specific delay number in the test?
              – Cody
              Nov 13 '18 at 0:20







            • 1




              with async for example
              – s1m0nw1
              Nov 13 '18 at 0:36
















            • Is there a way to not including the specific delay number in the test?
              – Cody
              Nov 13 '18 at 0:20







            • 1




              with async for example
              – s1m0nw1
              Nov 13 '18 at 0:36















            Is there a way to not including the specific delay number in the test?
            – Cody
            Nov 13 '18 at 0:20





            Is there a way to not including the specific delay number in the test?
            – Cody
            Nov 13 '18 at 0:20





            1




            1




            with async for example
            – s1m0nw1
            Nov 13 '18 at 0:36




            with async for example
            – s1m0nw1
            Nov 13 '18 at 0:36













            2














            The first issue in your code is that SimpleViewModel.coroutineContext has no Job associated with it. The whole point of making your view model a CoroutineScope is the ability to centralize the cancelling of all coroutines it starts. So add the job as follows (note the absence of a custom getter):



            class SimpleViewModel : ViewModel(), CoroutineScope 

            override val coroutineContext = Job() + Dispatchers.Unconfined

            var data = 0

            fun doSomething()
            launch
            delay(1000)
            data = 1





            Now your test code can ensure it proceeds to the assertions only after all the jobs your view model launched are done:



            class ScopedViewModelTest 

            @Test
            fun coroutineDelay()
            // Arrange
            val viewModel = SimpleViewModel()

            // ActTes
            viewModel.doSomething()

            // Assert
            runBlocking
            viewModel.coroutineContext[Job]!!.children.forEach it.join()

            Assert.assertEquals(1, viewModel.data)







            share|improve this answer

























              2














              The first issue in your code is that SimpleViewModel.coroutineContext has no Job associated with it. The whole point of making your view model a CoroutineScope is the ability to centralize the cancelling of all coroutines it starts. So add the job as follows (note the absence of a custom getter):



              class SimpleViewModel : ViewModel(), CoroutineScope 

              override val coroutineContext = Job() + Dispatchers.Unconfined

              var data = 0

              fun doSomething()
              launch
              delay(1000)
              data = 1





              Now your test code can ensure it proceeds to the assertions only after all the jobs your view model launched are done:



              class ScopedViewModelTest 

              @Test
              fun coroutineDelay()
              // Arrange
              val viewModel = SimpleViewModel()

              // ActTes
              viewModel.doSomething()

              // Assert
              runBlocking
              viewModel.coroutineContext[Job]!!.children.forEach it.join()

              Assert.assertEquals(1, viewModel.data)







              share|improve this answer























                2












                2








                2






                The first issue in your code is that SimpleViewModel.coroutineContext has no Job associated with it. The whole point of making your view model a CoroutineScope is the ability to centralize the cancelling of all coroutines it starts. So add the job as follows (note the absence of a custom getter):



                class SimpleViewModel : ViewModel(), CoroutineScope 

                override val coroutineContext = Job() + Dispatchers.Unconfined

                var data = 0

                fun doSomething()
                launch
                delay(1000)
                data = 1





                Now your test code can ensure it proceeds to the assertions only after all the jobs your view model launched are done:



                class ScopedViewModelTest 

                @Test
                fun coroutineDelay()
                // Arrange
                val viewModel = SimpleViewModel()

                // ActTes
                viewModel.doSomething()

                // Assert
                runBlocking
                viewModel.coroutineContext[Job]!!.children.forEach it.join()

                Assert.assertEquals(1, viewModel.data)







                share|improve this answer












                The first issue in your code is that SimpleViewModel.coroutineContext has no Job associated with it. The whole point of making your view model a CoroutineScope is the ability to centralize the cancelling of all coroutines it starts. So add the job as follows (note the absence of a custom getter):



                class SimpleViewModel : ViewModel(), CoroutineScope 

                override val coroutineContext = Job() + Dispatchers.Unconfined

                var data = 0

                fun doSomething()
                launch
                delay(1000)
                data = 1





                Now your test code can ensure it proceeds to the assertions only after all the jobs your view model launched are done:



                class ScopedViewModelTest 

                @Test
                fun coroutineDelay()
                // Arrange
                val viewModel = SimpleViewModel()

                // ActTes
                viewModel.doSomething()

                // Assert
                runBlocking
                viewModel.coroutineContext[Job]!!.children.forEach it.join()

                Assert.assertEquals(1, viewModel.data)








                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 16 '18 at 9:48









                Marko Topolnik

                145k19194322




                145k19194322



























                    draft saved

                    draft discarded
















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid


                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.

                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid


                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.

                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function ()
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53271646%2fhow-to-unit-test-coroutine-when-it-contains-coroutine-delay%23new-answer', 'question_page');

                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    27

                    Top Tejano songwriter Luis Silva dead of heart attack at 64

                    Category:Rhetoric