Photo by Anders Jildén on Unsplash

This post describes how to integrate Angular and WireMock (a mock HTTP back-end server). Protractor, along side Jasmine is used to create User Interface (UI) tests. At the same time, Protractor provides integration point between Angular and WireMock.

Table of Content

UI Testing

Building applications is easy. Building high quality, maintainable applications that bring business value is hard.

To help you business thrive, retain existing, and gain new users, you need to build quality right from the start.

Consider for a moment a mobile banking application you’re using. Imagine that, after the latest update you can’t log in or you can’t transfer money to/from your account. What is the value of that application at that moment?

Assuring the core functionality is working correctly, all the time, is of vital importance to your business. Testing helps anticipate problems before they end up in production and affect your users.

Testing Automation Pyramid

Testing Automation Pyramid, introduced by Mike Cohn in Succeeding with Agile Software Development in 2009, is a model used to describe test automation at three different levels. It provides a guideline on how many tests to have at each level, considering cost of implementation and test execution time in contrast to business value they provide.

Note: UI level is also known as End To End (E2E) and Service level is often called the Integration level. Alternative terms will be used henceforward.

Tesing Automation Pyramid; Martin Fowler, 2012

Tests should bring business value to your project and give you confidence to ship to the production. If that means more E2E tests than integration or unit tests, let it be.

Note: Similar “testing pyramid” concept was also introduced in 2006, by Jason Huggins

UI Testing Levels

Unit Test

At base, unit tests provide instant validation that the smallest bits of code works as expected. Unit tests are created in isolation (without any external dependencies) and have a single responsibility.

Integration Test

Testing interaction between bits of code that are combined in groups is done within the integration layer. Groups often require external collaborators to work correctly. External collaborators can be other components (service/directive/pipe) or other systems (database, file system or any system exposed through an API). Test stubs are often used to assist in integration testing.

UI integration tests confirms that the UI works correctly. User input should trigger the right action (e.g. service call), right data should be presented and the state should change accordingly.

E2E Test

At highest level, E2E tests assures the application is working correctly from the user’s perspective. It simulates real user actions through web browser with the entire application stack. Test stubs can be used to assist E2E testing.

WireMock Fundamentals

WireMock is an HTTP mock server. At its core it is web server that can be primed to serve canned responses to particular requests (stubbing) and that captures incoming requests so that they can be checked later (verification)

http://wiremock.org/docs/

WireMock also has a lot of useful features like:

  • proxying (selectively proxy requests to another service)
  • record and playback (create stub mappings from interaction with existing APIs)
  • response templating (use attributes of request in generating response)
  • simulating faults (fixed/random/global delays, bad responses)
  • stateful behaviour
  • handling HTTPS requests

It can be used as a standalone process or as a library inside any Java Virtual Machine (JVM) application and can be configured either via REST (JSON) interface and/or its JavaAPI, or via JSON files.

WireMock is frequently used in integration and/or E2E testing where communication with the real service is not possible or desirable.

Protractor Fundamentals

Protractor is an end-to-end test framework for Angular and AngularJS applications. Protractor runs tests against your application running in a real browser, interacting with it as a user would.

https://www.protractortest.org/

Protractor provides a wrapper around the WebDriverJS. WebDriverJS provides WebDriver APIs focused on controlling the behavior of web browser from the user’s point of view. WebDriver commands are asynchronous.

Protractor ensures that our test and web page are in sync (waits for angular and/or data to be available before executing next step in test). It also supports Angular specific locator strategies, which allows us to test Angular specific elements easily.

Jasmine Fundamentals

Jasmine is a behavior-driven development framework for testing JavaScript code. It does not depend on any other JavaScript frameworks. It does not require a DOM. And it has a clean, obvious syntax so that you can easily write tests.

https://jasmine.github.io/index.html

Jasmine provides three basic concepts:

  • Suits, which are used to describe your test and group related specs.
  • Specs, which contains expectations that test the state of the code.
  • Expectations, which validate the state.

Example suite showing previously mentioned concepts:

describe("A suite", function() {
  it("contains spec with an expectation", function() {
    expect(true).toBe(true);
  });
});

Integrating Angular and WireMock (via Protractor)

WireMock is started as a standalone instance and is used as a HTTP mock server. Protractor is configured to automatically start WireMock instance before executing tests. Protractor is also configured to stop WireMock instance after all tests are executed.

Process of integration is demonstrated on newly created Angular application.

Create a demo project

  • Create a new Angular application using Angular CLI
    $ ng new wiremock-demo

Download WireMock

  • Under wiremock-demo/e2e create a new wiremock-config folder$ mkdir wiremock-config
  • Download WireMock standalone under wiremock-demo/e2e/wiremock-config/$ curl https://repo1.maven.org/maven2/com/github/tomakehurst/wiremock-standalone/2.27.2/wiremock-standalone-2.27.2.jar --output wiremock-standalone.jar
  • Check that wiremock-standalone.jar can be started$ java -jar wiremock-standalone.jar --port 9000
  • Check that you can access the server from the new terminal window or a browser
    $ curl localhost:9000/api
  • If everything is OK you should see the following message
    No response could be served as there are no stub mappings in this WireMock instance.
  • Stop WireMock standalone by pressing Control + c

Configure WireMock to serve predefined responses

WireMock can be configured to serve predefined responses via

  • JSON over HTTP
  • JSON configuration file

In this example, JSON configuration file is used.

When started, WireMock server creates two folders under the current one (e.g. wiremock-demo/e2e/wiremock-config), mappings and __files. Folders are only created if they don’t already exist and won’t be deleted when the WireMock instance is stopped.

To serve predefined response and validate they are working fine:

  • Create a new hello-world.json file under mappings folder
  • Add following content to hello-world.json file
    {
        "request": {
            "method": "GET",
            "url": "/api/hello-world"
        },
        "response": {
            "status": 200,
            "body": "Hello World!"
        }
    }
  • Start previously stopped instance of WireMock standalone server
    $ java -jar wiremock-standalone.jar --port 9000
  • Validate that configured response is served
    $ curl localhost:9000/api/hello-world
  • If everything is OK you should see the following message
    Hello World!

Configure Protractor to start WireMock before executing tests

Protractor provides a lot of configuration options. One of them is a beforeLaunch() callback function. It is executed only once, before tests are started. It’s main purpose is to bring up test dependencies.

To start WireMock standalone before tests are executed, update protractor.conf.js with following content

  • Import function for spawning a new process
    const { spawn} = require('child_process')
  • Start a WireMock standalone before executing tests
    beforeLaunch: async function () {
        // spawns a new process 
        spawn(
          // the command to run
          'java',
          // list of string arguments
          ['-jar', './e2e/wiremock-config/wiremock-standalone.jar', '--port=9000'],
          // options is used to configure the pipes that are established between the parent and child process.
          { stdio: ['pipe', 'inherit', 'inherit']}
        );
  • Additional check is needed to confirm WireMock server is ready to serve responses. Updated beforeLaunch() with following content (add after spawning a new WireMock process)
    for (let i = 0; i < 5; i++) {
          try {
            const response = await fetch('http://localhost:9000/__admin/mappings')
            if (response.ok) {
              const data = await response.json();
              console.log("mappings: ", data)
              break;
            } else {
              throw new HTTPResponseError(response);
            }
          } catch (error) {
            console.log(error);
            await delay(1000)
          }
        }
  • Add node-fetch as a dependency
    npm i node-fetch --save
  • Import fetch
    const fetch = require("node-fetch");
  • Introduce a delay by adding following function to the end of the protractor.conf.js
    const delay = ms => new Promise(res => setTimeout(res, ms));

Configure Protractor to stop WireMock after executing tests

Protractor provides another callback function afterLaunch() which is called only once, before the program exits. It is called after all tests have finished running and the WebDriver instance has been shut down.

Update protractor.conf.js to shut down WireMock standalone instance after tests are executed.

  • Import function for synchronously spawning a new process
    const { spawnSync } = require('child_process')
  • Stop WireMock standalone after tests are done and WebDriver is closed
    afterLaunch: function () {
        // spawns a new synchronous process
        // the function will not return until the child process has been fully closed
        spawnSync('sh', ['-c', 'lsof -t -i :9000 | xargs kill -9'])
      }

Using Angular and WireMock (via Protractor)

Once WireMock is up and running, point you service to it (instead of the real back-end previously used).

Potential problems
  • Cross-Origin Request Blocked -> fix it by adding --enable-stub-cors flag when starting WireMock. It enables automatic sending of cross-origin response headers.

Conclusion

Testing at different levels is of crucial importance for the project success. It provides assurance that the application is working correctly and brings promised value. WireMock provides an excellent HTTP mock server that can be useful in integration and/or E2E tests.

Sources