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:

Monkey

1

2

3

functiongetValue(){

returnvalue;

}

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:

Monkey

1

2

3

4

5

6

7

(:test)

functioninitialStepsTotalIsZero(logger){

varview=newStepsCarouselMock();

varvalue=view.getValue();

Toybox.Test.assertEqualMessage(value,0.0,"Initial Steps Total is expected to be 0.0");

returntrue;

}

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 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:

Monkey

1

2

3

functiongetActivityMonitorInfo(){

returnToybox.ActivityMonitor.getInfo();

}

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:

Monkey

1

2

3

4

classActivityMonitorInfoMock{

varsteps=0;

varstepGoal=5000;

}

In the StepsCarouselMock we want to be able to inject the ActivityMonitorInfoMock, so let’s add the following to the StepsCarouselMock class:

Monkey

1

2

3

4

5

6

7

8

9

hidden varactivityMonitorInfo=newActivityMonitorInfoMock();

functiongetActivityMonitorInfo(){

returnactivityMonitorInfo;

}

functionsetActivityMonitorInfo(info){

activityMonitorInfo=info;

}

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!

Monkey

1

2

3

functiongetStepsRecorded(){

returnstepsRecorded;

}

Let’s create the unit test

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

Monkey

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

(:test)

functionwhenTimerDoesNotRunRecordedStepsDontIncrease(logger){

varview=newStepsCarouselMock();

//create the ActivityMonitorInfoMock

varactivityMonitorInfo=newActivityMonitorInfoMock();

view.setActivityMonitorInfo(activityMonitorInfo);

//set steps to15

activityMonitorInfo.steps=15;

//inject the ActivityMonitorInfoMock into the StepsCarouselMock

view.setActivityMonitorInfo(activityMonitorInfo);

//execute business logic

view.compute(null);

Toybox.Test.assertEqualMessage(activityMonitorInfo.steps,15,"Steps are 15");