Using Stubs and Mocks in Jasmine to test your Angular code

Setting up your environment and is a very important part of unit testing AngularJS code. We discuss some of the tools and methods under which we can provide the full workflow of a system under test in this post.

Intro

Unit tests allow us to automatically test our code under a variety of conditions to prove that it works and when it breaks it does so in expected ways. In order to predictably test code, we also need to completely control the setup and data provided to the code under test.

Thankfully, the test tools provided for Angular testing allow you to mimic your models and control how your code responds to code in a very precise way. Jasmine provides some easy way to create test doubles and even “spy” on their execution.

In this post we will discuss some of the best practices and tools we use to control the environment your code is working against.

Why do we need to fake stuff?

We need to control the entire environment and workflow to make sure we are testing the exact scenarios.

Messing around with the DB and creating fake records can be difficult at best, at worst it can create false positives. Even more common, some CI systems lack access to a DB at all

We also need the ability to re-create the conditions under which a bug has been created. sometimes this means we need to replicate some crazy conditions

There are two tools to make this happen: Test Stubs (also known as  Fakes, depending on who you ask) and Mock Objects:

  • Test stubs are a very simplified object or data structure designed specifically for the test scenario. These can be constructed at the beginning of the test or per test.
  • Mock objects are class instances that mimic an existing class to provide the same method interface and return specific values when a method call occurs.

Test Stubs in AngularJS & Jasmine

Test stubs are a simple data structure or model we rely on when running our tests. These can be as simple as a static array of data or a very lightweight object with publically scoped methods. To differentiate a stub from a mock, we typically only mimic the methods we are actually testing. This is quite useful during

More often than not a stub is created at the beginning of the test suites and made accessible to the suite’s tests. A good practice is to limit the modifications to the stubs to make sure you are always testing the same thing.

If you must make changes it is a good idea to make a local copy to isolates your modifications to a specific test.

Test Stub Examples

HeaderComponent

Below is an abridged version of the component code we are unit testing. The important thing to note: at the end of this method’s execution we make a call to the router service to navigate the user to the “login” route and we need to make sure this and only this method is executed.

/**
 * Simple component providing a navigation header.
 */
...
export class HeaderComponent implements OnInit {
    ...
    /**
     * clickLogout clears the user's creds but also 
     * takes the user to the Login route
     */
    clickLogout() {
        ...
        this.router.navigate(['/login']);
    }
    ...
}

Test Suite Code

Here is the abridged test suite where we create our test stub

...

/** 
 * beforeEach setup executes before each test suite test runs.
 *
 * This allows us to setup the stub before each test
 */
beforeEach(async(() => {

    ...
    
    TestBed.configureTestingModule({
        ...
        providers: [

        /**
         * Create a very basic stub object with one method:'navigate'
         *
         * Use Jasmine's createSpy to create a very basic function
         * which also allows us to "listen in" when it's called
         */
        {
            provide: Router,
            useClass: class { 
                navigate = jasmine.createSpy("navigate"); 
            }
        }
        ...
    ]})
    .compileComponents();

    ...

}));

/**
 * Here we test the method and make sure we actually navigate
 */
it('should navigate to /login when clickLogout is fired', () => {
   ...
   let router = fixture.debugElement.injector.get(Router);
   component.clickLogout();

   // "listen" to make sure that the navigate method has been 
   // called and it was called with the expected value
   expect(router.navigate).toHaveBeenCalledWith(["/login"]);
   ...
});

 

Mocks

A Mock object is a simulated object instance used to mimic a classes behavior using the same interface. In simpler terms, this is a fake class with the same method signature as the Real Thing.

A mock object can be composed of multiple objects (and sometimes multiple mock objects), but most often should be created as simply as possible to keep your tests easily maintained.

Mock Object Example

/**
 * Create a mock of an existing service
 * by simply extending it and overriding some 
 * of the methods you wish to use in your tests
 */
class MockAuthService extends AuthService {

    /**
     * This method is implemented in the AuthService
     * we extend, but we overload it to make sure we
     * return a value we wish to test against
     */
    isLoggedIn() {
        return false;
    }
}

...

beforeEach(async(() => {

    ...

    TestBed.configureTestingModule({

        ...

        providers: [

        /**
         * Inject our mocked service in place of AuthService
         */
        {
            provide: AuthService,
            useClass: MockAuthService
        },

        ...

    ]})
    .compileComponents();

    ...

}));


it('should navigate to /login when clickLogout is fired', () => {

   /**
    * Get the mocked service here from our fixture
    * and add a spyOn over-ride to pretend we have
    * a logged in user.
    */
   let service = fixture.debugElement.injector.get(AuthService);
   spyOn(service, 'isLoggedIn').and.returnValue(true);
   
});

Conclusion

Unit tests are only as good as the environment you can provide for your code under tests. It’s very important to be able to mimic and recreate the environment under which you have both positive and negative results for your code.

Unit Testing AngularJS Components & Services

Unit testing is a very important part of software development, and it is extremely important to Angular applications as they become increasingly more complex. We’ll explain how to set up your unit tests and describe what you can actually test with some examples.

Intro

Unit testing is a very important part of the software development process, especially as your applications become more and more complex. Making sure your code is robust and adaptable is an important aspect of being professional. This is no less true with your AngularJS application.

If you are reading this, you are most likely already sold on unit testing but here’s some benefits and reasons for writing and maintaining your unit tests:

  • The ability to quickly update your code and make sure you are able to regressively test your code.
  • Updating your unit tests to cover a fixed bug will prevent the bug from getting re-introduced!
  • Use a CI to make sure EVERYONE on your team is testing the code.

Angular CLI, Jasmine & KarmaJS

Here at Arroyo Labs we use the latest version of Angular CLI a lot. It’s a great way to start a project very quickly provides a great start for your application. It also gives you some boilerplate unit tests for each component and service you create using the CLI.

While we have explained Jasmine and KarmaJS in past blog articles, here is a quick overview explaining how these frameworks work together:

  • Jasmine
    • A very popular behavior driven Javascript testing framework with a very clean syntax.
  • KarmaJS
    • A framework that allows you to run your Jasmine tests on a device or headless browser. The test runner itself will provide a DOM with which your code is rendered and provides the API that Jasmine will interact with.

Setting up your tests

To create a unit test, you need to include a consistent environment in which to run your code under test and we need to control the required classes and variables that go into creating this environment. Thanks to the magic of dependency injection, we can mock up some classes used by our classes and code.

Let’s take a look at the ‘top’ portion of a unit test we use in our user-admin project (you can find the whole unit test in our repo):

/**
 * Import some basic testing classes and utilities from AngularJS's
 * core testing code. This is the minimum required code to create 
 * a basic unit test.
 */
import {
     async,
     getTestBed,
     TestBed
} from '@angular/core/testing';

/**
 * Import some specific test classes used to mock the 
 * http backend and connection classes
 */
import {
    MockBackend,
    MockConnection
} from '@angular/http/testing';

/**
 * Import the HTTP classes we use in our service
 */
import {
    BaseRequestOptions,
    Http,
    Response,
    ResponseOptions,
    XHRBackend
} from '@angular/http';

/**
 * Finally, we import the required custom classes we use in our service,
 * and the service we are testing as well.
 */
import { User }         from '../shared/models/user.model';
import { AuthService }  from './auth.service';

import { UsersService } from './users.service';

What should we test in a service?

To test a service, we have to understand the underlying concept they represent: isolating logic and code into a re-usable class. This means we have a class we can pass around in our application where we get and manipulate data in expected ways.

Keep in mind, we can really only test publically scoped variables and functions. The unit test itself is really only able to interact with your service in the same way that an application is able to.

So, we have these things to test:

  • What public variables get initialized when we create the service?
  • Do public variables update as we expect when we call public functions?

Example Service Unit Test: UserService

In our UserAdmin project, we have a service called UserService used to interact with and manipulate user data we get from the AJAX backend.

The service itself only has quite a few public methods (it’s a very important class!) so we will focus on just two of the suite’s tests of the same method `getUsers`:

getUsers should return an empty observable list when the ajax request is unsuccessful

This is a test to make sure we return an empty list of users if the AJAX response does not contain an encoded list of users. This is a negative test, we are making sure that we return an expected response even when the backend returns an error.

it('#getUsers should return an empty observable list when the ajax   request is unsuccessful', () => {

 // set up the mocked service to return an
 // unsuccessful response (no users, 500 status)
 // with a helper method
 usersBodyData.success = false;
 setupConnections(backend, {
     body: {
         body: usersBodyData // use the previously init'd var for consistent responses
     },
     status: 500
 });

 // set up our subscriptions to test results 
 // when we actually get a result returned
 service.users$.subscribe((res) => {
     if(res) {
         expect(res).toBeTruthy();
         expect(res.length).toEqual(0);
     }
 });

 service.total$.subscribe((res) => {
     if(res) {
         expect(res).toBeTruthy();
         expect(res).toEqual(0);
     }
 });

 // make the actual request!
 let res = service.getUsers();

});

getUsers should return an observable list of users and result total when the ajax request is successful

This test makes sure that we get an observable list of users if the AJAX response is successful. To make sure this happens, we mock the response as though we have a JSON encoded list of users.

it('#getUsers should return an observable list of users and result total when the ajax request is successful', () => {
 // set up the mocked service to return an
 // unsuccessful response (users, 200 status)
 // with a helper method
 setupConnections(backend, {
 body: {
 body: usersBodyData
 },
 status: 200
 });
 
 // set up our subscriptions to test results 
 // when we actually get a result returned
 service.users$.subscribe((res) => {
 if(res) {
 expect(res).toBeTruthy();
 expect(res.length).toEqual(2);
 }
 });
 
 service.total$.subscribe((res) => {
 if(res) {
 expect(res).toBeTruthy();
 expect(res).toEqual(2);
 }
 });
 
 // make the actual request, with some params to pass via query string
 // we will explore this in a future post
 let res = service.getUsers(42, 42, 'id', 'desc');
});

What should we test in a component?

A component, like a service, is also a re-usable piece of code but it also has a defined lifecycle from its parent class and a frontend element via it’s template and optional CSS.

So, here is what we have to test:

  • Does this component actually get constructed?
  • What public variables get initialized and what DOM elements get rendered when we create the component?
  • Do public variables update as we expect when we call public functions?

Like we have noted above when discussing testing a service, you can really only test things that have been scoped as public by the component itself. This does include rendered DOM elements and you should test that these elements appear and render as expected since these are made public by the component itself.

Example Component Unit Test: UserListComponent

Test that the component actually results in a rendered component

it('should create', () => {
   fixture.detectChanges();
   const compiled = fixture.debugElement.nativeElement;
   component.ngOnInit();
 
   component.users = [];
   component.total = 0;
 
   // do we have a component at all?
   expect(component).toBeTruthy();
 
   // create new user button
   expect(compiled.querySelector('.btn-info')).toBeTruthy();
 
   // do we have a table
   expect(compiled.querySelector('table')).toBeTruthy();
   expect(compiled.querySelectorAll('tr').length).toBe(2);
});

Test that we display a list of users with a mocked response

it('should display list of users', () => {
   fixture.detectChanges();
   const compiled = fixture.debugElement.nativeElement;
 
   setupConnections(backend, {
       body: {
           body: bodyData
       },
       status: 200
   });
 
   component.ngOnInit();
 
   // create new user button
   expect(compiled.querySelector('.btn-info')).toBeTruthy();
 
   fixture.detectChanges();
 
   // do we have a table with users?
   fixture.detectChanges();
   expect(compiled.querySelectorAll('tr').length).toBe(21);
 
   // do we see the expected page count
   expect(component.getPageCount()).toBe(2); 
});

Conclusion

Unit testing is a not a oft-celebrated portion of the software development cycle, but it is an important part of the process. Once you have good tests in place, you can pivot direction and update your code with an increased sense of security.

Testing a REST API with Frisby.js

When our web app depends on pulling data through a REST API, we need to test that the endpoints well. Thats where things become more complicated and tedious to do manually. Two great libraries, Jasmine.js and Frisby.js, can help us out here.

What is Frisby and how does it work?

We are software developers and we strive to write good code.  We try to ensure the quality of every line and workflow which in turn means testing.  Manual testing works to a point but without automated testing of REST APIs and Ajax endpoint regression testing becomes cumbersome.  Are you really going to test all the possibilities/permutations with Postman?  I didn’t think so.

Testing a REST API is not so different to testing modules, but to create “end-to-end” test cases we needed make real endpoints calls. A manual testing job quickly becomes a tedious task, so we need to find a way to make things automated.

Frisby is a REST testing framework built on node.js and jasmine, and makes testing API endpoints a very flexible and easy task.

As any normal npm package running in node, Frisby needs node.js installed first, after that you can install Frisby as a common package:

npm install -g frisby

By convention, the file name containing the Test Suite, should include the trailing word “_spec.js”, node.js runs in javascript so logically js is the extension.

In order to continue our intrduction we will study the roleAPI_spec.js, a test suite part of the package Users of Erdiko Framework:

var frisby = require('../node_modules/frisby/lib/frisby');

This is the “kickoff” for our test suite, we instance the library in a Frisby variable, it will be our object to along  the suite.

describe('Role api test suite', function() {
  var baseURL = 'http://docker.local:8088/ajax/users/roles/';
  /**---This case test the api response and check the result----*/

  frisby.create('Get all roles')
        .get(baseURL + 'roles')
        .expectStatus(200)
        .expectHeader('Content-Type', 'application/json')
        .afterJSON(function (response) {          
            expect(response.body.method).toBe('roles');
            expect(response.body.success).toBe(true);          
            expect(response.errors).toBe(false);
            expect(response.body.roles).toBeDefined();
        })     
        .toss()
});

All  Frisby test starts with frisby.create(‘A gently worded description of test’) and next we must specify the verb to make the call to our endpoint. It is obvious but must match with the endpoint structure.

In our case we used .get(baseURL + ‘roles’)

Differences between using post or get.

There are differences between verbs post and get. When we create post test cases, we should pass objects as arguments, let’s see an example grabbed from userAPI_spec.js from Users package of Erdiko framework:

/*---------------------------------------------------------*/
/*-----------------Create User success --------------------*/
frisby.create('Creation will success.')
 .post(baseURL + 'register',
 {"name":"NameTest",
  "email":"test@email.com",
  "password":"secret"},
 { json: true },
 post_header)
 .expectStatus(200)
 .toss()

By default, Frisby sends POST and PUT requests as application/x-www-form-urlencoded parameters. If we want to send a raw request content, we must use { json: true } as the third argument to the .post().

Using Expectations.

/**--checking creation with expectations --*/
frisby.create('Checking user created exist.')
    .get(baseURL + 'retrieve?id=' + USER_ID)
    .expectStatus(200)
    .afterJSON(function (response) {
        expect(response.body.method).toBe('retrieve');
        expect(response.body.success).toBe(true);
        expect(response.body.user.id).toBe(response.body.user.id);
        expect(response.body.user.email).toBe(newUser.email);
        expect(response.body.user.name).toBe(newUser.name);
        expect(response.body.user.role.id).toBe(newUser.role);
    })
   .toss()

In comparison with our traditional phpunit, the framework to create unit test cases in php, we could say that phpunit Asserts are analog to Frisby Expectations. In particular, we are interested by that expectations to handle json code. Because that we are use the ‘expect’ clause that compare the API response with a parameter provided by us:

expect(response.body.method).toBe('retrieve')

Here we expect that ‘response.body.method’ be equals to the string ‘retrieve’. It’s important to mention that the comparison is by type and value at same time and both should match to result ‘true’.

After explain that we have a few expectations used:

  • expectJSON( [path], json )
  • expectJSONTypes( [path], json )

Managing Headers, afterJSON()

/**-----------delete user created ----------------*/
frisby.create('removing user created.')
    .get(baseURL + 'cancel?id=' + USER_ID)
    .expectStatus(200)
    .afterJSON(function (response) {
        expect(response.body.method).toBe('cancel');
        expect(response.body.success).toBe(true);
        expect(response.body.user.id).toBe(USER_ID);
    })
   .toss()

As we said previously, Frisby is an extension of Jasmine.node, and afterJSON is a very good evidence because afterJSON is the extension of after() with the convenience of parse the body obtained as response as JSON values automatically. The ‘after’ occurs immediately after the response is sent from the endpoint call and is used as a callback.

Using inspectors, inspectJSON()

Inspector helpers are useful for viewing details about the HTTP response when the test does not pass, or has trouble for some reason. They are also useful for debugging the API itself as a more user-friendly alternative to curl.

// Console output
{ url: 'http://theurl/get?foo=bar',
    headers:
    { 'Content-Length': '',
        'X-Forwarded-Port': '80',
        Connection: 'keep-alive',
        Host: 'httphost.org',
        Cookie: '',
        'Content-Type': 'application/json' },
    args: { foo: 'bar'},
    origin: '127.0.0.1' }

Basically inspectJSON() prints the raw HTTP response, other similar and general inspectors are:

  • inspectBody()
  • inspectHeaders()
  • InspectRequest()
  • InspectStatus()

Conclusion

Frisby makes it possible to write end-to-end tests with a lot of flexible tools and the process of test a complete REST API turns fun and easy. We miss the ability to ‘serialize’ test cases as in a regular test unit framework. Given the nature of javascript its possible to chain nested test cases and mimic, in some aspect, that behaviors.

We haven’t tried Newman yet, but hope to soon.  Let us know about your Frisby (and Newman) experiences.

Unit testing your Javascipt with Jasmine, KarmaJS & Travis CI

Unit testing is an important part of any software project but until recently it was difficult to test your front-end javascript and oft neglected by developers. Using Jasmine, Karma and TravisCI we will show you how to painlessly automate your JS testing.

Javascript is used on over 93% of websites today and used to do all sorts of front end magic. Most likely, your interactions with the web are provided via Javascript.

With any luck you have moved beyond the anti-patterns of yore and you have abstracted your JS functionality into some coherent objects to maintain your sanity. Now it’s time to start testing these pieces of code.

Testing Tools

I explored javascript unit testing using three tools: Jasmine, Karma and TravisCI. Using all three in conjunction seems to get the best results for a larger team.

Jasmine

Jasmine is a very popular javascript unit testing framework, very similar to many other xUnit testing frameworks like phpUnit and jUnit. Many other testing tools like actually use the same syntax (if not use Jasmine outright) so this is a pretty good thing to learn about.

I should note, unlike many other xUnit frameworks, Jasmine is BDD or a Behavior Driven Development framework. This is a fancy way of saying you structure your test suites in a way where you test ‘behavior’ scenarios rather than simple methods and functions.

KarmaJS

Karma is a test runner that executes your unit tests in a ‘real browser’. In short, it runs your Jasmine tests in a real browser via websockets.

This means you can test using a headless browser like PhantomJS or a real browser like Chrome. This really becomes useful when you need to test in multiple browsers, but especially useful when you need to run your tests locally (via Chrome) or remotely on a CI (in our examples below we run the unit tests using Chromium. More on that later).

There’s a little setup involved with Karma, but not too much. Honestly, it took less time to get my Karma config created than it did to write my example unit tests.

Travis CI

Travis CI is a hosted continuous integration tool that will watch your repo and execute your tests on certain conditions like a push or a pull request. It is also is free for your open source, or at least your public, Git Hub repos. Best of all, you can also show a super cool ‘build status’ badges in your github readme page.

Travis CI: Using Chromium

Please note that if you run your KarmaJS tests locally using Chrome, you will need to make sure Travis CI knows to use the open source equivalent Chromium as it fit’s into its ‘free tier’ licensing scheme.

Below are the key things you need to set this up, all of this code can be found in our examples below:

.travis.yml config

We need to tell Travis to install the Chromium browser package when it creates the container. Add the noted entry (‘export CHROME_BIN=chromium-browser’) into the “before_install” section of your travis config.

...
before_install:
 - export CHROME_BIN=chromium-browser
...
karma.conf.js

Next we need to determine if our tests are executed by Travis, and if so, tell Travis to use Chromium (which is aliased to CHROME_BIN from our config above) when exec’ing our tests:

// Config values to allow TravisCI to run chrome in it's container
browsers: ['Chrome', 'ChromeCanary'],
   customLaunchers: {
   // tell TravisCI to use chromium when testing
   Chrome_travis_ci: {
      base: 'Chrome',
      flags: ['--no-sandbox']
   }
},

...

// Detect if this is TravisCI running the tests and tell it to use chromium
if(process.env.TRAVIS){
   config.browsers = ['Chrome_travis_ci'];
}

I think its easiest to look at config from our example code to see exactly where this is placed in your config: https://github.com/arroyolabs-blog/movie-vote/blob/master/karma.conf.js

Example Project: Movie Vote

I created a very basic ‘web app’ (this is a stretch) to learn more about using Jasmine/Karma/CI with javascript.

It’s a simple JS class where a user can up or downvote a movie. While it includes a very basic html page you can run in your browser to see the class in action, the real magic is in the testing.

Here is a link to the Travis CI page showing our passing tests: https://travis-ci.org/arroyolabs-blog/movie-vote

Example Project 2: ng2 3 page SPA

If you are using ng-cli, karma is already configured for you. Running it is super easy and the hardest part is finding a CI solution that works for you.

Check out our super simple example and review the Travis Config to see this in action: https://travis-ci.org/arroyolabs-blog/effective-funicular

Conclusion

Testing is a very important part of software development and can be fun and easy once you get things set up to run automatically. Hopefully after reading this post you will have a good idea of how to integrate unit testing into your JS projects.