How to execute unit tests

Hi all,

I want to write (unit) tests for my widget, but I must be doing some stupid and hopefully simple mistake.

Here is my test code; it is in file "TestHowToTest.mc":
class TestHowToTest
{

(:test)
static function testHowToTest(logger)
{
Test.assertEqual(1,1);
return true;
}

}


I compile with this statement; maybe the "-t" flag is at the wrong place?:
/.../EclipseSDK/connectiq-sdk-mac-2.1.3/bin/monkeyc -t -o /.../TestUnitTest/bin/TestUnitTest.prg -w -y /.../key.. -z /.../TestUnitTest/resources/drawables/drawables.xml:/.../TestUnitTest/resources/layouts/layout.xml:/.../TestUnitTest/resources/strings/strings.xml -m /.../TestUnitTest/manifest.xml /.../TestUnitTest/source/TestHowToTest.mc /.../TestUnitTest/source/TestUnitTestView.mc /.../TestUnitTest/source/TestUnitTestApp.mc -d fr735xt_sim


Monkeyc also logs this warning: "WARNING: UnitTests:11: Class 'RunNoEvil' does not initialize its super class, 'Test'", but I don't know how to fix this.

I start the simulator like this:
/.../connectiq-sdk-mac-2.1.3/bin/connectiq


I try to execute the tests with this command:
/.../connectiq-sdk-mac-2.1.3/bin/monkeydo /.../TestUnitTest/bin/TestUnitTest.prg /t


What happens then is that the simulator window remains "blank" and the terminal outputs this:
...
File pushed successfully
Connection Finished
Closing shell and port
Found Transport: tcp
Connecting...
Connecting to device...
Device Version 0.1.0
Device id 1 name "A garmin device"
Shell Version 0.1.0



I do all this on a Mac.

I have tried a lot of different flavors of the code and the command line commands, but with no success.

Does anyone know where my error is? Of course, I have read the documentation about how to test, but maybe I miss something...

Thanks,
Florian
  • Monkeyc also logs this warning: "WARNING: UnitTests:11: Class 'RunNoEvil' does not initialize its super class, 'Test'", but I don't know how to fix this.

    You don't. It appears to be a bug with the RunNoEvil class, which you have no control over. I filed a bug on this last week.

    I compile with this statement; maybe the "-t" flag is at the wrong place?

    No, the -t argument should be fine there. Your compile line is a little weird though...

    /.../EclipseSDK/connectiq-sdk-mac-2.1.3/bin/monkeyc -t -o /.../TestUnitTest/bin/TestUnitTest.prg -w -y /.../key.. -z /.../TestUnitTest/resources/drawables/drawables.xml:/.../TestUnitTest/resources/layouts/layout.xml:/.../TestUnitTest/resources/strings/strings.xml -m /.../TestUnitTest/manifest.xml /.../TestUnitTest/source/TestHowToTest.mc /.../TestUnitTest/source/TestUnitTestView.mc /.../TestUnitTest/source/TestUnitTestApp.mc -d fr735xt_sim


    A path that begins with / refers to the root of your file system, and a path with ... is one directory up from the named directory. It doesn't make sense for a path to begin with /... as that means to look one directory up from the root of the file system. Are you using ellipses to represent parts of the path you've pulled out?

    Assuming you have an environment variable set to the absolute path to the ConnectIQ SDK, perhaps like this...

    export SDKROOT=/connectiq-sdk-mac-2.1.3


    Have you tried compiling from within the project directory like this...

    cd TestUnitTest
    ${SDKROOT}/bin/monkeyc -t -o bin/TestUnitTest.prg -w -y key -z resources/drawables/drawables.xml:resources/layouts/layout.xml:resources/strings/strings.xml -m manifest.xml source/TestHowToTest.mc source/TestUnitTestView.mc source/TestUnitTestApp.mc -d fr735xt_sim


    Then start the simulator like this...

    ${SDKROOT}/bin/connectiq


    And run the test program like this

    ${SDKROOT}/bin/monkeydo bin/TestUnitTest.prg /t


    Does that work?

    Travis
  • Hi Travis,

    thanks for the quick and detailed response.

    I have caused some confusion: I have "shortened" the initial part of the paths in my posted commands to make it a bit more readable. On my computer, I used the fully qualified paths and so the result is still the same with your statements.

    But since you have given such a detailed response and since you have already reported a related bug, it sounds like you can in principle run tests (even on 2.1.3). Can you post a very simple example test case file? Are you using a class, a module or some other way? Maybe my test itself is in some way wrong.

    Thanks & regards,
    Florian
  • Yes, I've not really had problems with building and running tests. I use a batch file (evil.bat) to simplify building and running, but here is the output

    C:\Users\Travis\workspace\Test>evil build fenix3 --debug --warn --test && evil start && evil run fenix3 --test
    Connect IQ compiler version 2.1.3
    monkeyc.bat -w -t -y developer.key -o bin\Test.prg -m manifest.xml -z resources\resources.xml -d fenix3 source\TestApp.mc
    WARNING: The launcher icon (30x30) is smaller than the specified launcher icon size of the device 'fenix3' (40x40). Padding will be added to the image to center it.
    WARNING: UnitTests:11: Class 'RunNoEvil' does not initialize its super class, 'Test'
    connectiq.bat

    Waiting for 0 seconds, press CTRL+C to quit ...
    monkeydo.bat bin\Test.prg fenix3 /t
    Found Transport: tcp
    Connecting...
    Connecting to device...
    Device Version 0.1.0
    Device id 1 name "A garmin device"
    Shell Version 0.1.0
    Copying file.... 2% complete
    ...
    Copying file.... 100% complete
    File pushed successfully
    Connection Finished
    Closing shell and port
    Found Transport: tcp
    Connecting...
    Connecting to device...
    Device Version 0.1.0
    Device id 1 name "A garmin device"
    Shell Version 0.1.0
    ------------------------------------------------------------------------------
    Executing test Class.test_static_class_method...
    DEBUG (20:17): abc
    WARNING (20:17): def
    ERROR (20:17): ghi
    PASS
    ------------------------------------------------------------------------------
    Executing test Module.test_module_method...
    DEBUG (20:17): abc
    WARNING (20:17): def
    ERROR (20:17): ghi
    PASS
    ------------------------------------------------------------------------------
    Executing test test_global_function...
    DEBUG (20:17): abc
    WARNING (20:17): def
    ERROR (20:17): ghi
    PASS

    ==============================================================================
    RESULTS
    Test: Status:
    Class.test_static_class_method PASS
    Module.test_module_method PASS
    test_global_function PASS
    Ran 3 tests

    PASSED
    Connection Finished
    Closing shell and port

    C:\Users\Travis\workspace\Test>


    Here is the test part of the code...

    using Toybox.Test as Test;

    class Class
    {
    (:test)
    static function test_static_class_method(logger)
    {
    logger.debug("abc");
    logger.warning("def");
    logger.error("ghi");
    Test.assertEqual(1,1);
    return true;
    }
    }

    module Module
    {
    (:test)
    function test_module_method(logger)
    {
    logger.debug("abc");
    logger.warning("def");
    logger.error("ghi");
    Test.assertEqual(1,1);
    return true;
    }
    }

    (:test)
    function test_global_function(logger) {
    logger.debug("abc");
    logger.warning("def");
    logger.error("ghi");
    Test.assertEqual(1,1);
    return true;
    }
  • Hi Travis,

    thanks again for your detailed help.

    I still have the same behavior and no tests get executed. Do you think it is possible that I somehow get your demo unit test project? I believe there must be some slight difference compared to my project and I would really like to write some unit tests. I feel guilty for publishing a widget and not having even a single piece of code covered by automated tests ;-).

    Thanks & regards,
    Florian
  • You can download the project from here. I changed it a little bit to compile out debugging code that is not used in normal builds.

    If I'm testing, the compile and run lines look like this...

    monkeyc -w -t -y developer.key -o bin\Test.prg -m manifest.xml -z resources\resources.xml -d fr735xt source\TestApp.mc
    monkeydo.bat bin\Test.prg fr735xt /t


    If I'm building the app normally (I want to compile out all of the test/debug code), I build and run like this...

    monkeyc -w -x debugOnly -y developer.key -o bin\Test.prg -m manifest.xml -z resources\resources.xml -d fr735xt source\TestApp.mc
    monkeydo.bat bin\Test.prg fr735xt /t


    To be able to effectively test your code it is helpful to ensure that classes be loosely coupled (have little direct knowledge of each other), so you'll want to keep this in mind when writing your code. By reducing direct dependencies, you make it possible to create mock objects (they implement the interface but not the behavior of the normal class) for testing and hook them up to your application classes for testing.

    Travis
  • Hi Travis,

    I am not getting tired of thanking you for your support.

    On my Mac, the tests of your project are not executed just like my own tests are not executed, i. e. the console stays idle after the output of

    Device id 1 name "A garmin device"
    Shell Version 0.1.0


    Now I have finally set up a Garmin dev environment on a Windows virtual machine and there the tests are executed. I have now contacted Garmin as I believe it is a bug in theirs tools happening only on Macs. I hope they can solve it.


    With regards to your advise with regards to loose coupling: How to you implement "interfaces". Do you create classes with empty method implementations and then create two subclasses: One for the "productive" implementation and one for the mock? That is the only option I can think of in Monkey C, but I strongly hope that I am missing some capabilities ;-).

    Regards,
    Florian
  • With regards to your advise with regards to loose coupling: How to you implement "interfaces".

    With a duck-typed language like MonkeyC, you don't really need to create proper interfaces. As far as I see it, they are only useful to document the expectations the user class must adhere to (I believe that Ui.InputDelegate is an example of this). Given the cost of creating a class and minimal resources available, I don't bother with interfaces.

    The big thing is to not access information directly in another class (don't violate encapsulation) and try avoid situations where you know the type of another class (usually when creating them). As an example, it is nice to make it so that the only the controller knows the actual type of the model or view classes. The model and view can both be given a reference to a controller, but they don't really know that it is a MyTestController or a MyAppController. This allows you to test the view and model easily. Of course it is still going to be difficult to test the controller, but you should be able to fix that by giving the controller access to a class factory.

    I'm not exactly sure how far I'm willing to go when writing application classes and tests. I'm not really using the test framework that much at this point.
  • Hi all,

    I bring this issue up again asking if anyone succeeded in running unit tests on a Mac environment? On Windows, it is working for me...

    Unfortunately, Garmin did not yet reply to my bug report, which I have sent 1-2 weeks ago.

    Regards,
    Florian
  • Apologies for not getting to this more quickly! We are aware of the issue you've reported, and have it in our list of items to investigate.
  • Apologies for not getting to this more quickly! We are aware of the issue you've reported, and have it in our list of items to investigate.


    Thanks for the information. I am waiting eagerly :D.