Tutorial: Create Connect IQ unit tests

In the previous tutorials we learned how to set up our project to be able to do unit testing and how to run unit tests.
To understand this article it’s rather essential that you’ve read the previous 2 articles, in case you didn’t read those articles yet I’d advise to go back and read it now, start with the article how to create a unit test project.
.

Prerequisites for this tutorial

This tutorial is part of a tutorial series:
This tutorial continues where the last tutorial left of. If you did that tutorial you can continue with your own code base or you can download my project here instead.

First Unit Test: Exposing private variables

Purpose of the test

In our first actual connect IQ unit test we want to verify that the intial value of the Total Steps is set to 0.

Problem

A problem is that we need access to the total steps value to verify it, but that field is private (hidden in MonkeyC terms)…

Solution

The solution is quite easy as our mock StepsCarouselMock inherits from StepsCarouselView we can expose the private variable value in a public getter getValue(). So we add the following function to our mock:
We now have the ability to actually get the value, now let’s go and create the actual unit test:
  • In the file StepsCarouselTests.mc add the code for the first test:
Ok there’s only 4 lines in our test but why are they there? Let’s go over them and explain what they do:
  • var view = new StepsCarouselMock(); -> create a new instance of our mocked StepsCarouselView
  • var value = view.getValue(); -> get the value of the field “value”, in StepsCarouselView this variable is private, but we’ve exposed it’s value in the getValue() function.
  • Toybox.Test.assertEqualMessage(… -> compare the actual value with the expected value and define the message to show when the test fails
  • return true; -> if a test function returns false then the test fails, you can misuse this functionality to do your assertions in this return statement, but personally I prefer to asserts with actual assert functions because I think it’s cleaner and more obvious when a test fails.

Run the tests

Create a run test profile for your project and execute the test, in the console window you should see the following pass by (for each watch target): And also in the summary “run no evil” window you’ll see 2 succeeding tests!

Second unit test: Mock external dependencies

Purpose of the test

we want to verify that when the timer is not running that recorded steps do not increase.

Problem

The problem here that we want to test the compute function, but inside that function there is a dependency on the behaviour of an external component (Toybox.ActivityMonitor)

Solution

The solution for this issue is to replace this external dependency by something that we can access. We will in other words have to create a mock for the Toybox.ActivityMonitor and instead of calling the real one call our mock instead. In our business project we still want to call the real one though…

In the StepsCarouselView.mc file we introduce a new function getActivityMonitorInfo, this function will return the real value of the component: In the StepsCarouselView.mc file we replace the calls to Toybox.ActivityMonitor.getInfo() by a call to our newly created function. (replace in the compute function and in the onTimerStart function).

When unit testing we don’t want to depend on this external dependency

So let’s create a new mock which has the same fields as the real function.

Edit the file StepsCarouselMocks.mc and add a mock for the ActivityMonitorInfo… something like this should do it: In the StepsCarouselMock we want to be able to inject the ActivityMonitorInfoMock, so let’s add the following to the StepsCarouselMock class: We also need to be able to get the recorded steps from the StepsCarouselMock, we learned how to do this in the first unit test, expose the private!

Let’s create the unit test

Ok we’re finally ready to create the test now:

Want some practice?

If you want some practice, try to create the following unit tests:
  • when the timer runs, then recorded steps should increase
  • when the timer is stopped, then the total steps (of the day) are shown
  • when the timer is stopped, then the label does not change after 5 seconds
  • every 5 seconds the value of the label should change (create the test + fix the bug)

Source Code

You can download the full source code from github

Feedback

Did you like this article?Questions?
  • Post in the comments section below!

Leave a Reply