3
votes

Related to this question: Uncaught TypeError: Cannot use 'in' operator to search for 'SUPPORT_COOKIES' in null

But different due to Jasmine Test-Tool and the general question about Best-Practices for JS-Java Adapter-Testing.


We are working on setting up a development environment for a group of developers including continuous integration, build and automated testing.

For this purpose, we need to have a way to call the WL Adapters on the WL servers from stand-alone test cases/clients (test code) that runs independent from the actual Worklight App or any Worklight client. These tests will have to run at deploy and continuous build and will test the adapters.

We came up with this solution, because there is no way to test the Adapters locally, before they are deployed. Also, we can not really include test code into our adapters and deploy the code together with the Adapter. This would not be a nice solution and we would have test code on the server with the Adapter.

We will probably use Jasmine and JUnit as our test tools and I tried to setup a stand-alone Jasmine Worklight Client/Test by including all Worklight JS libraries and variables (that the worklight compiler adds to the final compiled and deployed App .html) to my Jasmine test.

It runs for some part and it seems to initialize ok:

wlclient init started worklight.js:1112
before: app init onSuccess worklight.js:1112
after: app init onSuccess worklight.js:1112
wlclient init success

but when I want to execute WL.Client.invokeProcedure(invocationData, I get the error:

TypeError: Cannot use 'in' operator to search for 'SUPPORT_COOKIES' in null

So there seems to be some configuration or initialization missing in my stand-alone Worklight Client/Test. Can anyone of the experts tell me what that could be?

<head>
    <title>Jasmine Spec Runner</title>
 <link rel="stylesheet" type="text/css" href="lib/jasmine-1.3.1/jasmine.css">

<script>
        // Define WL namespace.
        var WL = WL ? WL : {};

        /**
         * WLClient configuration variables.
         * Values are injected by the deployer that packs the gadget.
         */


        WL.StaticAppProps = {
         "APP_DISPLAY_NAME": "app",
   "APP_SERVICES_URL": "\/tests\/",
   "APP_VERSION": "1.0",
   "ENVIRONMENT": "preview",
   "HEIGHT": 460,
   "LOGIN_DISPLAY_TYPE": "popup",
   "LOGIN_POPUP_HEIGHT": 610,
   "LOGIN_POPUP_WIDTH": 920,
   "PREVIEW_ENVIRONMENT": "common",
   "WIDTH": 320,
       "WORKLIGHT_ROOT_URL": "\/tests\/"
    };



<script src="lib/common/js/wljq.js"></script>
        <script src="lib/common/js/base.js"></script>
        <script src="lib/common/js/containerCommunicationAPI.js"></script>
        <script src="lib/wlclient/js/messages.js"></script>
        <script src="lib/common/js/wlcommon.js"></script>
        <script src="lib/common/js/busy.js"></script>
        <script src="lib/wlclient/js/diagnosticDialog.js"></script>
        <script src="lib/wlclient/js/deviceAuthentication.js"></script>
        <script src="lib/wlclient/js/window.js"></script>
        <script src="lib/wlclient/js/worklight.js"></script>
        <script src="lib/wlclient/js/gadgetCommunicationAPI.js"></script>
        <script src="lib/wlclient/js/wlclient.js"></script>
        <script src="lib/wlclient/js/wlfragments.js"></script>
        <script src="lib/wlclient/js/encryptedcache.js"></script>
        <script src="lib/wlclient/js/jsonstore/jsonstore.js"></script>
        <script src="lib/wlclient/js/challengeHandlers/antiXSRFChallengeHandler.js"></script>
        <script src="lib/wlclient/js/challengeHandlers/authenticityChallengeHandler.js"></script>
        <script src="lib/wlclient/js/challengeHandlers/deviceAuthAutoProvisioningChallengeHandler.js"></script>
        <script src="lib/wlclient/js/challengeHandlers/deviceAuthNoProvisioningChallengeHandler.js"></script>
        <script src="lib/wlclient/js/challengeHandlers/remoteDisableChallengeHandler.js"></script>
            <script src="../apps/app/common/js/jquery-1.10.1.min.js"></script>
    <!-- script>window.$ = window.jQuery = WLJQ;</script-->
    <script src="../apps/app/common/jqueryMobile/jquery.mobile-1.3.1.js"></script>

    <script src="../apps/app/common/js/initOptions.js"></script>
     <script src="../apps/app/common/js/messages.js"></script>


    <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine.js"></script>
    <script type="text/javascript" src="lib/jasmine-1.3.1/jasmine-html.js"></script>

    <!-- include source files here... -->

    <!--<script type="text/javascript" src="../apps/app/common/js/knockout-2.2.1.js"></script>-->
    <!--<script type="text/javascript" src="../apps/app/common/js/knockout.mapping-latest.js"></script>-->
    <!--<script type="text/javascript" src="../apps/app/common/js/globalize.js"></script>-->
    <!--<script type="text/javascript" src="../apps/app/common/js/app.js"></script> -->
    <!--<script type="text/javascript" src="../apps/app/common/js/common.js"></script>-->
    <!--<script type="text/javascript" src="../apps/app/common/js/date.js"></script>-->
    <!--<script type="text/javascript" src="../apps/app/common/js/localize.js"></script>-->
    </script>-->


    <!-- include spec files here... -->
    <!--script type="text/javascript" src="spec/SpecHelper.js"></script-->
    <script type="text/javascript" src="spec/TestSpec.js"></script>

July 15 2013:

Got Jasmine automatically running in WL Client this way:

var wlInitOptions = {

    connectOnStartup : false,

};

if (window.addEventListener) {
    window.addEventListener('load', function() { WL.Client.init(wlInitOptions); execJasmine(); }, false);
} else if (window.attachEvent) {
    window.attachEvent('onload',  function() { WL.Client.init(wlInitOptions); execJasmine(); });
}

function wlCommonInit(){


}

var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;

var htmlReporter = new jasmine.HtmlReporter();

jasmineEnv.addReporter(htmlReporter);

jasmineEnv.specFilter = function(spec) {
    return htmlReporter.specFilter(spec);
};

function execJasmine() {
    WL.Logger.debug("ExecJasmine");
    jasmineEnv.execute();
}

But now I am getting Access Control Problems because my WL-Jasmine-Test-Client is running on an Apache on port 80 and the Adapters on the WL Server on port 8080.

running test worklight.js:1112
Application did not define an i18n messages object, skipping translation. worklight.js:1112
wlclient init started worklight.js:1112
before: app init onSuccess worklight.js:1112
after: app init onSuccess worklight.js:1112
wlclient init success worklight.js:1112
ExecJasmine worklight.js:1112
Request [http://XXX:8080/apps/services/api/app/desktopbrowser/query] worklight.js:1112
running test 2 worklight.js:1112
OPTIONS http://XXX:8080/apps/services/api/app/desktopbrowser/query 401 (Unauthorized) base.js:883
OPTIONS http://XXX:8080/apps/services/api/app/desktopbrowser/query Origin http://XXX is not allowed by Access-Control-Allow-Origin. base.js:883
XMLHttpRequest cannot load http://XXX:8080/apps/services/api/app/desktopbrowser/query. Origin http://XXX is not allowed by Access-Control-Allow-Origin. SpecRunnerAdapter.html:1
Refused to get unsafe header "X-JSON" base.js:994
[http://XXX:8080/apps/services/api/app/desktopbrowser/query] Host is not responsive. worklight.js:1112
{"invocationContext":null,"errorCode":"UNRESPONSIVE_HOST","errorMsg":"The service is currently not available."} worklight.js:1112
Refused to get unsafe header "X-JSON" 

using this WL-Test-Client app configuration:

        WL.StaticAppProps = {
   "APP_DISPLAY_NAME": "app",
   "APP_SERVICES_URL": "http:\/\/XXX:8080\/apps\/services\/",
   "APP_VERSION": "1.0",
   "ENVIRONMENT": "desktopbrowser",
   "HEIGHT": 460,
   "LOGIN_DISPLAY_TYPE": "popup",
   "LOGIN_POPUP_HEIGHT": 610,
   "LOGIN_POPUP_WIDTH": 920,
   "WIDTH": 320,
   "WORKLIGHT_ROOT_URL": "http:\/\/XXX:8080\/apps\/services\/api\/app\/desktopbrowser\/"
};

I guess we will stop here and simply use the INVOKE service on the WL-Server to call wl_unprotected Adapter Procedures for our JUnit test in a test environment.

Then figure out how to secure the procedures for production at build/deploy and at the same time remove the unit test executions - since they won't work with secured Adapters anymore.

2

2 Answers

1
votes

For unit testing your best option is to use WL adapter invocations service. No client code at all, just make an HTTP(s) request to the WL server:

http://pic.dhe.ibm.com/infocenter/wrklight/v6r0m0/index.jsp?topic=%2Fcom.ibm.worklight.help.doc%2Fdevref%2Fc_adapter_invocation_service.html

1
votes

Assuming you want Unit Tests for your adapter code, like the title of the question specified, I would note the following:

  • The scope of Unit Tests are a single unit of code, in JavaScript that usually means a small function. You test if sum(1,2) returns 3. You do not automatically open a URL that shows your calculator application, simulates user input to click on buttons (1, 2 and =), waits for the calculation event to return 3 and the test code reads the output from the DOM. The former describes a Unit Test, the latter describes an Integration/Functional Test. If you want Functional Tests, read this. If you want Unit Tests, keep reading.

  • Mock everything related to third-party APIs (e.g. Worklight, jQuery, Dojo). These APIs have already been tested and are known to work, at the very least that's a reasonable assumption to make. For example, when you write JUnit tests you assume everything from the standard Java library works as specified in the documentation. Sinon.js is a great library for creating JavaScript mocks, stubs and spies.

  • Use either Mozilla Rhino or Node.js to read and eval the JavaScript for your adapter's JavaScript implementation file (e.g. myCalculatorAdapter-impl.js). There are many good ways to create the assertions, for example Mocha if you go the Node.js route.

Here's some example code, imagine this is my adapter procedure:

myCalculatorAdapter-impl.js

function sum (a, b) {
    WL.Logger.debug({hello: 'world'});
    return a+b;
}

Imagine the following contains my unit tests for said adapter procedure:

test.js

//Mocks
var WL = {};
WL.Logger = {};
WL.Logger.debug = function (msg) {
    console.log(msg);
};

//Node.js Libraries
var fs = require('fs'),
    assert = require('assert');

//Read the adapter code
eval(fs.readFileSync('./myCalculatorAdapter-impl.js').toString());

//Do assertions -- Unit testing
assert.equal(sum(1, 2), 3, '1+2 should be 3');

I run it with: node test.js. If I change the implementation to subtract instead of add (return a-b) when I run it I'm alerted of the failure AssertionError: 1+2 should be 3.

I used the standard assertion library that Node.js provides, you may want to use something with more features. Similarly you may want to use a library for mocking third party APIs or things that don't apply to the current test. If you're doing network communication, you will want to mock the inputs.

Surely there are others ways to test adapters, feel free to experiment and share.