3

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.

Community
  • 1
  • 1
christianmenkens
  • 790
  • 4
  • 22

2 Answers2

1

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

Anton
  • 3,166
  • 1
  • 13
  • 12
  • thank you for your answer. From the typical eclipse development work, we are of course aware of the invocations service. This is great for development and initial testing, but not good for a continuous build. E specially due to the requirement that you have to change the security settings of the adapter e.g. securityTest="wl_unprotected". We need the adapter to be deployed in production-ready configuration with all security, realms, etc. – christianmenkens Jul 13 '13 at 21:05
  • first of all, you don't have to use wl_unprotected, this is just for convenience. Note, however, that in case you do not use it - you will have to implement client logic within your mock client. Also, adapter invocation service is not dev only, it is a fully production feature of WL server. – Anton Jul 14 '13 at 07:21
  • Thank you for your answer. I see, I did not recognize that when reading the InfoCenter, thank you. Now, it would of course be great to get some examples on how to deal with the standard WL Security features from Mobile Security Tests such as XSRF etc. and Custom AdapterAuthenticator ChallengeHandler in Java - but I assume something like this is not provided. - maybe we will go with the wl_unprotected and figure out how to maintain two adapter config files. thx. – christianmenkens Jul 15 '13 at 17:38
  • https://www.ibm.com/developerworks/community/blogs/worklight/entry/understanding_predefined_worklight_authentication_realms_and_security_tests11?lang=en – Anton Jul 16 '13 at 18:32
1

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.

cnandreu
  • 5,113
  • 3
  • 25
  • 50
  • Thank you for your answer. You are totally right, the definitions between Unit test and Functional Test blend very much in my question, but this is due to the fact that our Adapters have Units of Code that need to be tested that also use compiled Adapter-Java code to execute their functionality. The approach with the JS runtimes Rhino and Node are very elegant for JS Unit testing ... thank you for your ideas and details to your approach. – christianmenkens Jul 15 '13 at 16:22
  • Nevertheless, due to the fact we need AdapterJS and AdapterJava Unit tested together, we would probably have to go with Mozilla Rhino locally for JS<->Java ... not sure if this is not an overkill to include Rhino into our Test-Infrastructure "just to test adapters". I would think to use the real WL Server and it's Rhino for at least the JS<->Java functionality is more elegant and lightweight on testing. Also, by introducing our own Rhino into testing we are of course creating a different infrastructure than we have on the WL server - the tests could not be representative. – christianmenkens Jul 15 '13 at 16:23
  • thank you for your link to functional testing with WL Test Workbench in WL 6. I am sorry, I forgot to mention that we are not moving to 6 in our project. We will continue working with 5.0.6, at least till a first release. – christianmenkens Jul 15 '13 at 16:29
  • I would recommend using small functions that only do *one thing*. Maybe you can abstract away the Java code calls into JavaScript functions that can be mocked. That way you can test the JavaScript code with Node or Rhino and the Java code with JUnit. – cnandreu Jul 15 '13 at 16:31
  • Thank you for your response. We do have small functions that do one thing - but one complicated thing that needs to use for example a Java Calender and TimeZones to do conversions/calculations etc. - mocking all the Java code would be too much work - we will see, maybe we use the INVOKE service, then we can test functional too - maybe we find some other way to split JS and Java...but this is almost impossible due to being quite interwoven. Thank you for your suggestions and comments. – christianmenkens Jul 15 '13 at 19:45