Harrington Web

Mock HTTP calls with Angular and Protractor

Friday, February 17, 2017
Mocking Data

One of the benefits using AngularJS is also using some test tools like Protractor. Protractor is a tool that was made for Angular testing. Many times when testing you will need to mock some data so that you can see if your application responds in the correct manner. Let us assume that we have the following HTML file running our application.
<!doctype html>
<html ng-app="demo">
    <head>
        <title>Hello</title>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.1/angular-mocks.js"></script>
        <script src="hello.js"></script>
    </head>

    <body>
        <div ng-controller="Hello">
            <p>Hello <span id="gname">{{exampleResponse.name}}</span></p>
        </div>
    </body>
</html>
This web application loads a few javascript files from a CDN (content delivery network) and also a "hello.js". In order to get mocking to work we have to make sure that there is a reference to "angular-mocks.js".
Then under "hello.js" we define a simple HTTP request that will get some data and return it in the $scope.exampleResponse variable. This response is exactly what we want to mock.
angular.module('demo', [])
.controller('Hello', function($scope, $http) {
    $http.get('http://baseUrl/exampleResponse').
        then(function(response) {
            $scope.exampleResponse = response.data;
        });
});
Install some basic packages for protractor.
npm install protractor jasmine-core httpbackend
Setup for protractor-conf.js
exports.config = {

    directConnect: true,
    //seleniumAddress: 'http://localhost:4444/wd/hub',

    suites: { "main" : ["spec/**/*.js"]},

    capabilities: {
        "browserName": "chrome"
    },

    baseUrl: "http://localhost:8000/",

    framework: 'jasmine2',

    jasmineNodeOpts: {
        defaultTimeoutInterval: parseInt(process.env.TIMEOUT) || 30000,
        verbose: true,
        realtimeFailure: true,
        //print: function() {}
    },

    resultJsonOutputFile: 'result.json',

    onPrepare: function() {},

    afterLaunch: function(exitCode) {}
};
The test script, now we can have some fun. The first thing we can do is require httpbackend. There is another way to accomplish mock however httpbackend provides a really simple means of getting the job done. For this example we also create a before and after hook that will allow us to define backend and also clear and teardown the variable.
var HttpBackend = require('httpbackend');
var backend;

describe('A test', function() {
    beforeEach(() => {
        backend = new HttpBackend(browser);
    });

    afterEach(function() {
        backend.clear();
    });

    it('should diplay Ben as the name', function() {
        backend.whenGET(/exampleResponse/).respond({"name": "Ben"});
        browser.get('http://localhost:8000');
        expect($('#gname').getText()).toBe("Ben");
    });
})
If you remember from back in the "hello.js" we did a GET request. "$http.get('http://baseUrl/exampleResponse')" Within the it statement, this is where we can have backend tell Angular that when it needs this GET request send this new data. "backend.whenGET(/exampleResponse/).respond({"name": "Ben"});"
That is all there is too it. For more information and other HTTP handlers like POST, check our the documentation on httpbackend https://www.npmjs.com/package/httpbackend. You can also take a look at an example of this work on my github and play with it yourself. https://github.com/blh0021/protractor-angular-demos

Build your own expectation library.

Monday, February 13, 2017
In the world of impending deadlines, testing is a key a vital role with any project. When building software you can test many functions and processes in a matter of minutes depending on how you setup your tests and how well the code was written to be testable. A key element for that tests is what you expect. Javascript for example, there are many expectation frameworks out there. Jasmine, Chai, Mocha to name a few. Let's build a simple expectation without their input.
var expect = function(obj) {
  return {}
}
This will be our starting point for what we will expect. Very simply it is a function that will accept an argument obj and then it returns some object back that will be defined in more detail soon. You may be thinking one object? That doesn't seem quite right. For an expect statement to work shouldn't we have something like "expect(a, b, 'equal')". Yes, we could do something like that. However, for this example, we will attempt to make the code read like a sentence. For example "expect(a).to.beEqual(b)". For this to work, we will create a singleton class out of the object that gets returned. The first item we will need is what value that we are wanting to test. This can be our expectation.
var expect = function(obj) {
  return {
    "expectation": obj  
  }
}
Once we have that value we need to create an object "to". This is really just for readability and organization.
var expect = function(obj) {
  return {
    "expectation": obj,
    "to": {}  
  }
}
Lastly, we need to create a function with the "to" object called beEqual(val).
var expect = function(obj) {
  return {
    "expectation": obj,
    "to": {
      "beEqual": function(val) {}
    }  
  }
}
At this point, you can run your expect statement and no matter what you put into it, the statement will pass. Depending on your test framework and how you use it this can be a real problem. The issue on this one is that we have not written any code to cause a failure yet. However as a reminder when you write your tests make sure that they will actually fail for the right reason. Tests that always pass are not necessarily useful. Hopping off the soap box and back into beEqual. In this function, we want to do 2 main things. The first is to check if the value in beEqual(val) is equal to the obj in the expect statement. The second is what to do if it is or is not equal. If they are equal we want to return true. If the answer turns out to be false, we want to throw an error and probably return some kind of message about why it failed as well.
var expect = function(obj) {
  return {
    "expectation": obj,
    "to": {
      "beEqual": function(val) {
        if (obj === val) {
          return true;
        } else {
          throw "Expected: " + val + " Got: " + obj;
        }
      }
    }  
  }
}
Congratulations you now have a very simplistic expectation framework that you can add too and modify to your hearts desire. There are many more opportunities to explore in this area. If you are new to coding etc, I suggest try writing a few more matchers that you can expect. Some examples toBeGreater, toBeLess, or maybe add a "not" object in there somewhere. Where ever it takes you I hope it ends up with better more solid code.

JSON Schema testing with Postman

Thursday, October 27, 2016

Postman is becoming quite the popular tool for accessing and testing REST api services. One of the cool features is that you can write some Javascript tests on your responses. Built into Postman is also the Tiny Validator v4 and Cheerio. Cheerio is a small core JQuery like implementation. The problem I have is that there is no way to import a schema file from a remote place or a file. This will be about how I get around that issue.

Pre-request Script


var uri='http://someUrl.com';

$.get(uri + '/someFolder/schema.json', function(schema) {
    postman.setEnvironmentVariable('schema', JSON.stringify(schema));
});

This Pre-request script is what saves the day. There are a couple of timing issues that are present due to async processing. If you were to put this in your test script your chances of it not completing in time are very high and you would get inconsistent results. If you are familiar with JQuery the $.get should look familiar as a ajax call that does a get request to a schema file. One thing to note it that what ever means you are using to serve the file you will need the proper Access-Control-Allow-Origin set. In most cases this will be set so that only that domain can access the file. If you were running this call from nodejs for example you wouldn't have this issue. Postman however is a Chrome app. Its basically running as a webpage. Please note that setting the Access-Control-Allow-Origin on a server can be a dangerous thing. Only set it where properly monitored and administered.

The Tests


var schema = JSON.parse(environment["schema"]);

var jsonData = JSON.parse(responseBody);

var results = tv4.validateMultiple(jsonData, schema);
for (var i = 0; i < results.errors.length; i++) {
      tests[JSON.stringify(results.errors[i])] = false;
}

The tests are fairly simple. We read in the schema that was input in the Pre-request and read in the response from the query and parse them into JSON objects. Then we run tv4.validateMultiple on them. In most examples you would just run tv4.validate. However I wanted to see each failure issue within the JSON data. In order to fail a test you would just set the tests array at a certain position to false. To make it understandable I put the error message as the key to the array and set it to false. Each one that is false will now show up under the tests when you run the query.

Final Thoughts

You have to note that this is a work around. It does have its potential issues. For example the promise for the get request could take longer than it takes to get response and the validate step. Or Access-Control-Allow-Origin opens up potential security hole. Use your best judgement.

Javascript send text function

Thursday, September 29, 2016
Simple send text function that you could use for front end tests within a browser. Replacing the value is quite easy with standard Javascript, however the DOM does not realize that the value has changed. For instance lets say you had an input field that is required to be filled in. If you just change the value by its self it will never remove the required message. To complete this we fire off a change event.
function sendText(id, val) {
  //find the element
  var el = document.getElementById(id);

  //replace the value of the element with val
  el.value = val;

  //create an event to let the DOM know something changed.
  var event = new Event('change');

  //fire off the event
  el.dispatchEvent(event);
}

Display Guitar Chords on webpage

Wednesday, September 14, 2016
Sometimes its nice to have a handy tool for displaying different chords on a web page. For instance lets say that you want to show off some lyrics with chords to teach someone a song. I created ChordTab to have a simple way to display those chords.

Code Sample

<style>
svg {
    width: 100px;
}
</style>

<c-tab chord="g" instrument="guitar">
<script src="https://gitcdn.xyz/repo/blh0021/chordTab/master/src/tab.min.js"></script>

Sample Chord


Other Instruments

In addition to guitar, you can also change the instrument attribute to mandolin, ukulele, or banjo. Currently not every chord has been created data wise but work is being done all the time.

Protractor getText from hidden element.

Thursday, August 25, 2016
Ran across a point that I wanted to get the value of a hidden element while running my Protractor tests. getText() function was not helpful at all. In order for it to return text it has to be visible. There is an option called getAttribute('textContent'). This will allow you to always return the text.

Sample HTML

<span style="display: none" name="hiddenSpan">Hidden Text</span>

Sample Protractor JS

$('[name="hiddenSpan"]').getText();
// returns ""
$('[name="hiddenSpan"]').getAttribute('textContent');
// returns "Hidden Text"

Get a little Rusty

Tuesday, July 26, 2016

Been playing around with Rust lang and I have to say I am starting to really enjoy it as a low level programming language.  Its kinda cool to have some modern functionality from the beginning.  Now as a software developer I can get excited but what about being a test developer.  One should probably think those are the same thing.  Well I wont start up that argument but I tend to believe they are one in the same.

Lets do some basic coding. We can create us a new library called test_example.
$ cargo new test_example

This will create you a basic structure for a project. Included will be a src directory. Underneath that you will see lib.rs. You can open this up in your favorite text editor and see something that looks like.
#[cfg(test)]
mod test {
    #[test]
    fn it_works() {
    }
}
So you could run this right now and get some passing tests.
$ cargo test
Lets simplify what we are looking a little more. Change your lib.rs to look like.
pub fn truthy() -> bool {
    return true;
}

#[test]
fn its_truthy() {
    assert!(truthy())
}
Now you have a test that checks for the truthiness of truthy. Happy developing. On your red mark, get set on green, refactor...