Best approach for unit-testing scoped viewmodels












1















When dealing with coroutines inside a viewModel is best to have said viewModel implement CoroutineScope so all coroutines are cancelled when the viewModel is cleared. Usually I see coroutineContext defined as Dispatchers.Main + _job so that coroutines are executed in the main UI thread by default. Usually this is done on a open class so that all your viewModels can extend it and get the scope without boilerplate code.



The issue arises when trying to unit test said viewModels as Dispatchers.Main is not available and trying to use it throws an exception. I am tryin to find a good solution that doesn't involve external libraries or too much boiler plate on the child viewModels.



My current solution is to add the maincontext as a contructor paramenter with the Dispatchers.Main as the default value. Then in the unit test, before testing the viewModel I set it to Dispatchers.Default. I don't quiet like this solution as it exposes the coroutineContext implementation details for everyone to see and change:



open class ScopedViewModel(var maincontext = Dispatchers.Main) : ViewModel(), CoroutineScope {
private val _job = Job()
override val coroutineContext: CoroutineContext
get() = maincontext + _job

override fun onCleared() {
super.onCleared()
_job.cancel()
}
}
class MyViewModel : ScopedViewModel() {}


In the tests:



fun setup(){
viewModel = MyViewModel()
viewModel.maincontext = Dispacther.Default
}









share|improve this question



























    1















    When dealing with coroutines inside a viewModel is best to have said viewModel implement CoroutineScope so all coroutines are cancelled when the viewModel is cleared. Usually I see coroutineContext defined as Dispatchers.Main + _job so that coroutines are executed in the main UI thread by default. Usually this is done on a open class so that all your viewModels can extend it and get the scope without boilerplate code.



    The issue arises when trying to unit test said viewModels as Dispatchers.Main is not available and trying to use it throws an exception. I am tryin to find a good solution that doesn't involve external libraries or too much boiler plate on the child viewModels.



    My current solution is to add the maincontext as a contructor paramenter with the Dispatchers.Main as the default value. Then in the unit test, before testing the viewModel I set it to Dispatchers.Default. I don't quiet like this solution as it exposes the coroutineContext implementation details for everyone to see and change:



    open class ScopedViewModel(var maincontext = Dispatchers.Main) : ViewModel(), CoroutineScope {
    private val _job = Job()
    override val coroutineContext: CoroutineContext
    get() = maincontext + _job

    override fun onCleared() {
    super.onCleared()
    _job.cancel()
    }
    }
    class MyViewModel : ScopedViewModel() {}


    In the tests:



    fun setup(){
    viewModel = MyViewModel()
    viewModel.maincontext = Dispacther.Default
    }









    share|improve this question

























      1












      1








      1








      When dealing with coroutines inside a viewModel is best to have said viewModel implement CoroutineScope so all coroutines are cancelled when the viewModel is cleared. Usually I see coroutineContext defined as Dispatchers.Main + _job so that coroutines are executed in the main UI thread by default. Usually this is done on a open class so that all your viewModels can extend it and get the scope without boilerplate code.



      The issue arises when trying to unit test said viewModels as Dispatchers.Main is not available and trying to use it throws an exception. I am tryin to find a good solution that doesn't involve external libraries or too much boiler plate on the child viewModels.



      My current solution is to add the maincontext as a contructor paramenter with the Dispatchers.Main as the default value. Then in the unit test, before testing the viewModel I set it to Dispatchers.Default. I don't quiet like this solution as it exposes the coroutineContext implementation details for everyone to see and change:



      open class ScopedViewModel(var maincontext = Dispatchers.Main) : ViewModel(), CoroutineScope {
      private val _job = Job()
      override val coroutineContext: CoroutineContext
      get() = maincontext + _job

      override fun onCleared() {
      super.onCleared()
      _job.cancel()
      }
      }
      class MyViewModel : ScopedViewModel() {}


      In the tests:



      fun setup(){
      viewModel = MyViewModel()
      viewModel.maincontext = Dispacther.Default
      }









      share|improve this question














      When dealing with coroutines inside a viewModel is best to have said viewModel implement CoroutineScope so all coroutines are cancelled when the viewModel is cleared. Usually I see coroutineContext defined as Dispatchers.Main + _job so that coroutines are executed in the main UI thread by default. Usually this is done on a open class so that all your viewModels can extend it and get the scope without boilerplate code.



      The issue arises when trying to unit test said viewModels as Dispatchers.Main is not available and trying to use it throws an exception. I am tryin to find a good solution that doesn't involve external libraries or too much boiler plate on the child viewModels.



      My current solution is to add the maincontext as a contructor paramenter with the Dispatchers.Main as the default value. Then in the unit test, before testing the viewModel I set it to Dispatchers.Default. I don't quiet like this solution as it exposes the coroutineContext implementation details for everyone to see and change:



      open class ScopedViewModel(var maincontext = Dispatchers.Main) : ViewModel(), CoroutineScope {
      private val _job = Job()
      override val coroutineContext: CoroutineContext
      get() = maincontext + _job

      override fun onCleared() {
      super.onCleared()
      _job.cancel()
      }
      }
      class MyViewModel : ScopedViewModel() {}


      In the tests:



      fun setup(){
      viewModel = MyViewModel()
      viewModel.maincontext = Dispacther.Default
      }






      android unit-testing kotlin kotlinx.coroutines android-viewmodel






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Nov 26 '18 at 10:28









      Eric MartoriEric Martori

      879411




      879411
























          1 Answer
          1






          active

          oldest

          votes


















          2














          Personally I copied a solution from RxJava2: if your test runs against RxJava2 flow which includes two or more different schedulers, you want, sure, all of them to actually run in a single thread.
          Here is how it is done with RxJava2 testing:



          @BeforeClass
          public static void prepare() {
          RxJavaPlugins.setComputationSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setSingleSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxAndroidPlugins.setMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
          }


          I did the same for coroutines. Just have created a class which collects dispatchers, but these dispatchers can be changed.



          object ConfigurableDispatchers {

          @JvmStatic
          @Volatile
          var Default: CoroutineDispatcher = Dispatchers.Default

          @JvmStatic
          @Volatile
          var Main: MainCoroutineDispatcher = Dispatchers.Main

          ...
          }


          And, inside @BeforeClass method I call



          @ExperimentalCoroutinesApi
          fun setInstantMainDispatcher() {
          Main = object : MainCoroutineDispatcher() {
          @ExperimentalCoroutinesApi
          override val immediate: MainCoroutineDispatcher
          get() = this

          override fun dispatch(context: CoroutineContext, block: Runnable) {
          block.run()
          }
          }
          }


          That will guarantee that the block will be executed in the calling thread.



          It is the only alternative I found to constructor injection.






          share|improve this answer


























          • Given your dispatch implementation, you could return this from immediate.

            – Marko Topolnik
            Nov 26 '18 at 10:55











          • You're right. I'll edit the post. Thank you

            – Andrey Ilyunin
            Nov 26 '18 at 10:56













          • Would just Unconfined work without a custom implementation?

            – Marko Topolnik
            Nov 26 '18 at 10:56











          • If you mean MainCoroutineDispatcher, then worth mentioning that Dispatchers.Unconfined has a type of CoroutineDispatcher, whereas MainCoroutineDispatcher is a subtype of CoroutineDispatcher

            – Andrey Ilyunin
            Nov 26 '18 at 10:58






          • 1





            The type is there for a reason, that's for sure. It's an optimization measure. The only question is whether you gain anything by statically exposing it. Another point: immediate will never be used if you always return true from isDispatchNeeded.

            – Marko Topolnik
            Nov 26 '18 at 11:17














          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%2f53479143%2fbest-approach-for-unit-testing-scoped-viewmodels%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          2














          Personally I copied a solution from RxJava2: if your test runs against RxJava2 flow which includes two or more different schedulers, you want, sure, all of them to actually run in a single thread.
          Here is how it is done with RxJava2 testing:



          @BeforeClass
          public static void prepare() {
          RxJavaPlugins.setComputationSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setSingleSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxAndroidPlugins.setMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
          }


          I did the same for coroutines. Just have created a class which collects dispatchers, but these dispatchers can be changed.



          object ConfigurableDispatchers {

          @JvmStatic
          @Volatile
          var Default: CoroutineDispatcher = Dispatchers.Default

          @JvmStatic
          @Volatile
          var Main: MainCoroutineDispatcher = Dispatchers.Main

          ...
          }


          And, inside @BeforeClass method I call



          @ExperimentalCoroutinesApi
          fun setInstantMainDispatcher() {
          Main = object : MainCoroutineDispatcher() {
          @ExperimentalCoroutinesApi
          override val immediate: MainCoroutineDispatcher
          get() = this

          override fun dispatch(context: CoroutineContext, block: Runnable) {
          block.run()
          }
          }
          }


          That will guarantee that the block will be executed in the calling thread.



          It is the only alternative I found to constructor injection.






          share|improve this answer


























          • Given your dispatch implementation, you could return this from immediate.

            – Marko Topolnik
            Nov 26 '18 at 10:55











          • You're right. I'll edit the post. Thank you

            – Andrey Ilyunin
            Nov 26 '18 at 10:56













          • Would just Unconfined work without a custom implementation?

            – Marko Topolnik
            Nov 26 '18 at 10:56











          • If you mean MainCoroutineDispatcher, then worth mentioning that Dispatchers.Unconfined has a type of CoroutineDispatcher, whereas MainCoroutineDispatcher is a subtype of CoroutineDispatcher

            – Andrey Ilyunin
            Nov 26 '18 at 10:58






          • 1





            The type is there for a reason, that's for sure. It's an optimization measure. The only question is whether you gain anything by statically exposing it. Another point: immediate will never be used if you always return true from isDispatchNeeded.

            – Marko Topolnik
            Nov 26 '18 at 11:17


















          2














          Personally I copied a solution from RxJava2: if your test runs against RxJava2 flow which includes two or more different schedulers, you want, sure, all of them to actually run in a single thread.
          Here is how it is done with RxJava2 testing:



          @BeforeClass
          public static void prepare() {
          RxJavaPlugins.setComputationSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setSingleSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxAndroidPlugins.setMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
          }


          I did the same for coroutines. Just have created a class which collects dispatchers, but these dispatchers can be changed.



          object ConfigurableDispatchers {

          @JvmStatic
          @Volatile
          var Default: CoroutineDispatcher = Dispatchers.Default

          @JvmStatic
          @Volatile
          var Main: MainCoroutineDispatcher = Dispatchers.Main

          ...
          }


          And, inside @BeforeClass method I call



          @ExperimentalCoroutinesApi
          fun setInstantMainDispatcher() {
          Main = object : MainCoroutineDispatcher() {
          @ExperimentalCoroutinesApi
          override val immediate: MainCoroutineDispatcher
          get() = this

          override fun dispatch(context: CoroutineContext, block: Runnable) {
          block.run()
          }
          }
          }


          That will guarantee that the block will be executed in the calling thread.



          It is the only alternative I found to constructor injection.






          share|improve this answer


























          • Given your dispatch implementation, you could return this from immediate.

            – Marko Topolnik
            Nov 26 '18 at 10:55











          • You're right. I'll edit the post. Thank you

            – Andrey Ilyunin
            Nov 26 '18 at 10:56













          • Would just Unconfined work without a custom implementation?

            – Marko Topolnik
            Nov 26 '18 at 10:56











          • If you mean MainCoroutineDispatcher, then worth mentioning that Dispatchers.Unconfined has a type of CoroutineDispatcher, whereas MainCoroutineDispatcher is a subtype of CoroutineDispatcher

            – Andrey Ilyunin
            Nov 26 '18 at 10:58






          • 1





            The type is there for a reason, that's for sure. It's an optimization measure. The only question is whether you gain anything by statically exposing it. Another point: immediate will never be used if you always return true from isDispatchNeeded.

            – Marko Topolnik
            Nov 26 '18 at 11:17
















          2












          2








          2







          Personally I copied a solution from RxJava2: if your test runs against RxJava2 flow which includes two or more different schedulers, you want, sure, all of them to actually run in a single thread.
          Here is how it is done with RxJava2 testing:



          @BeforeClass
          public static void prepare() {
          RxJavaPlugins.setComputationSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setSingleSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxAndroidPlugins.setMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
          }


          I did the same for coroutines. Just have created a class which collects dispatchers, but these dispatchers can be changed.



          object ConfigurableDispatchers {

          @JvmStatic
          @Volatile
          var Default: CoroutineDispatcher = Dispatchers.Default

          @JvmStatic
          @Volatile
          var Main: MainCoroutineDispatcher = Dispatchers.Main

          ...
          }


          And, inside @BeforeClass method I call



          @ExperimentalCoroutinesApi
          fun setInstantMainDispatcher() {
          Main = object : MainCoroutineDispatcher() {
          @ExperimentalCoroutinesApi
          override val immediate: MainCoroutineDispatcher
          get() = this

          override fun dispatch(context: CoroutineContext, block: Runnable) {
          block.run()
          }
          }
          }


          That will guarantee that the block will be executed in the calling thread.



          It is the only alternative I found to constructor injection.






          share|improve this answer















          Personally I copied a solution from RxJava2: if your test runs against RxJava2 flow which includes two or more different schedulers, you want, sure, all of them to actually run in a single thread.
          Here is how it is done with RxJava2 testing:



          @BeforeClass
          public static void prepare() {
          RxJavaPlugins.setComputationSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setIoSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setNewThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxJavaPlugins.setSingleSchedulerHandler(scheduler -> Schedulers.trampoline());
          RxAndroidPlugins.setMainThreadSchedulerHandler(scheduler -> Schedulers.trampoline());
          }


          I did the same for coroutines. Just have created a class which collects dispatchers, but these dispatchers can be changed.



          object ConfigurableDispatchers {

          @JvmStatic
          @Volatile
          var Default: CoroutineDispatcher = Dispatchers.Default

          @JvmStatic
          @Volatile
          var Main: MainCoroutineDispatcher = Dispatchers.Main

          ...
          }


          And, inside @BeforeClass method I call



          @ExperimentalCoroutinesApi
          fun setInstantMainDispatcher() {
          Main = object : MainCoroutineDispatcher() {
          @ExperimentalCoroutinesApi
          override val immediate: MainCoroutineDispatcher
          get() = this

          override fun dispatch(context: CoroutineContext, block: Runnable) {
          block.run()
          }
          }
          }


          That will guarantee that the block will be executed in the calling thread.



          It is the only alternative I found to constructor injection.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 26 '18 at 10:56

























          answered Nov 26 '18 at 10:49









          Andrey IlyuninAndrey Ilyunin

          1,234223




          1,234223













          • Given your dispatch implementation, you could return this from immediate.

            – Marko Topolnik
            Nov 26 '18 at 10:55











          • You're right. I'll edit the post. Thank you

            – Andrey Ilyunin
            Nov 26 '18 at 10:56













          • Would just Unconfined work without a custom implementation?

            – Marko Topolnik
            Nov 26 '18 at 10:56











          • If you mean MainCoroutineDispatcher, then worth mentioning that Dispatchers.Unconfined has a type of CoroutineDispatcher, whereas MainCoroutineDispatcher is a subtype of CoroutineDispatcher

            – Andrey Ilyunin
            Nov 26 '18 at 10:58






          • 1





            The type is there for a reason, that's for sure. It's an optimization measure. The only question is whether you gain anything by statically exposing it. Another point: immediate will never be used if you always return true from isDispatchNeeded.

            – Marko Topolnik
            Nov 26 '18 at 11:17





















          • Given your dispatch implementation, you could return this from immediate.

            – Marko Topolnik
            Nov 26 '18 at 10:55











          • You're right. I'll edit the post. Thank you

            – Andrey Ilyunin
            Nov 26 '18 at 10:56













          • Would just Unconfined work without a custom implementation?

            – Marko Topolnik
            Nov 26 '18 at 10:56











          • If you mean MainCoroutineDispatcher, then worth mentioning that Dispatchers.Unconfined has a type of CoroutineDispatcher, whereas MainCoroutineDispatcher is a subtype of CoroutineDispatcher

            – Andrey Ilyunin
            Nov 26 '18 at 10:58






          • 1





            The type is there for a reason, that's for sure. It's an optimization measure. The only question is whether you gain anything by statically exposing it. Another point: immediate will never be used if you always return true from isDispatchNeeded.

            – Marko Topolnik
            Nov 26 '18 at 11:17



















          Given your dispatch implementation, you could return this from immediate.

          – Marko Topolnik
          Nov 26 '18 at 10:55





          Given your dispatch implementation, you could return this from immediate.

          – Marko Topolnik
          Nov 26 '18 at 10:55













          You're right. I'll edit the post. Thank you

          – Andrey Ilyunin
          Nov 26 '18 at 10:56







          You're right. I'll edit the post. Thank you

          – Andrey Ilyunin
          Nov 26 '18 at 10:56















          Would just Unconfined work without a custom implementation?

          – Marko Topolnik
          Nov 26 '18 at 10:56





          Would just Unconfined work without a custom implementation?

          – Marko Topolnik
          Nov 26 '18 at 10:56













          If you mean MainCoroutineDispatcher, then worth mentioning that Dispatchers.Unconfined has a type of CoroutineDispatcher, whereas MainCoroutineDispatcher is a subtype of CoroutineDispatcher

          – Andrey Ilyunin
          Nov 26 '18 at 10:58





          If you mean MainCoroutineDispatcher, then worth mentioning that Dispatchers.Unconfined has a type of CoroutineDispatcher, whereas MainCoroutineDispatcher is a subtype of CoroutineDispatcher

          – Andrey Ilyunin
          Nov 26 '18 at 10:58




          1




          1





          The type is there for a reason, that's for sure. It's an optimization measure. The only question is whether you gain anything by statically exposing it. Another point: immediate will never be used if you always return true from isDispatchNeeded.

          – Marko Topolnik
          Nov 26 '18 at 11:17







          The type is there for a reason, that's for sure. It's an optimization measure. The only question is whether you gain anything by statically exposing it. Another point: immediate will never be used if you always return true from isDispatchNeeded.

          – Marko Topolnik
          Nov 26 '18 at 11:17






















          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.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53479143%2fbest-approach-for-unit-testing-scoped-viewmodels%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

          Costa Masnaga

          Fotorealismo

          Sidney Franklin