Implementing Consumer-Driven Contract tests in Angular

Overview

Consumer-driven means that the Consumer is responsible for specifying the interactions that it expects with the provider. Interactions are described in detail: HTTP verb, endpoint path, params, response status code, resource fields used, etc. and all of them together form the consumer contract.

Consumer contract tests exercises the Client code

We do not define “hand-craft” directly the contracts because they could get out of sync quickly and not reflect the real interaction that the consumer code performs. Instead what we do is unit test the layer of our consumer that interacts with the provider (the Client or Adapter code) using the Pact JS library to specify the interactions. If the unit tests pass a Pact contract file is generated with the contract between consumer and provider.

Implementing Consumer Contract tests with Pact

In an Angular front-end application, Consumer Contract tests are implemented using Jasmine and the Pact JS library.
They are similar to other unit tests for logic making HTTP requests. The difference is that instead of mocking the network responses using the Angular MockBackend

mockBackend.connections.subscribe(connection => {
connection.mockRespond(new Response(new ResponseOptions({body: RELEASE_TOGGLES})));
});

we allow the requests to go through the network against the pact-mock-service which verifies and replies according the expected interaction.

  describe('Delete Task', () => {

    beforeAll((done) => {
      provider.addInteraction({
        state: 'a task with task-id exists',
        uponReceiving: 'a request to delete that with task-id',
        withRequest: {
          method: 'DELETE',
          path: '/tasks/task-id'
        },
        willRespondWith: {
          status: 204
        }
      }).then(done, e => done.fail(e));
    });

    it('should call the API to delete the task', (done) => {
      tasksService.delete('task-id').then(done);
    });

  });

Flexible Matching

It is vital that tests specify which are the resource fields the UI app cares about. But to not make tests rigid we’ll usually assert only the name and type of each field, not their exact content. To help with this, the Pact JS library provides flexible matching by regular expressions or type.

Next test is an example of a GET interaction where the expected resource and fields are defined using the type matcher Pact.Matchers.somethingLike.

  describe('Get all Tasks', () => {

    beforeAll((done) => {
      provider.addInteraction({
        state: 'tasks exists',
        uponReceiving: 'a request to get tasks',
        withRequest: {
          method: 'GET',
          path: '/tasks'
        },
        willRespondWith: {
          status: 200,
          headers: {'Content-Type': 'application/json'},
          body: [{
            id: Pact.Matchers.somethingLike('an id'),
            name: Pact.Matchers.somethingLike('a name'),
            done: Pact.Matchers.somethingLike(false),
            userId: Pact.Matchers.somethingLike('an user id')
          }]
        }
      }).then(done, e => done.fail(e));
    });

    it('should return all tasks from API', (done) => {
      const tasks: Task[] = [{
        id: 'an id',
        name: 'a name',
        done: false,
        userId: 'an user id'
      }];

      tasksService.getAll().then(tasks => {
        expect(tasks).toEqual(tasks);
        done()
      });
    });
  });

The mock server will return [{id: 'an id’, name: 'a name’, priority: 2, done: false}] in the consumer tests, but when the Pact verification is run in the provider, it will just check that the type of the name value is a String, and that the type of the priority value is a Number, etc.

To reiterate, the resource returned by the provider probably has other properties, but we should only describe the ones that the consumer uses.

Guidelines for state and uponReceiving descriptions

When we define the expected interactions between a consumer and a provider the ‘state’ and ‘uponReceiving’ descriptions used are important and should be written in a consistent style.

Later on, on the provider side, Pact uses the uponReceiving description to name the auto-generated test, and the state description is used to find a correspondent test state.

// Expected Interaction
provider.addInteraction({
  state: 'tasks exists',
  uponReceiving: 'a request to delete that tasks',
  withRequest: { method: 'DELETE', path: '/tasks/task-id' },
  willRespondWith: { status: 204 }
});

But the contracts are not only used by the tests, we also intend them to be user-friendly. Developers can consult contracts in the Pact Broker who formats them in a nice documentation style.

Sample scenarios

Note that the state affects what the producer should do and the uponReceiving affects the request that the consumer makes

Successful interaction where data is returned
state: ‘tasks for project with id project-id exist’,
uponReceiving: ‘a request to get tasks for that project’

Successful interaction where no data is returned
state: ‘tasks for project with id project-id do not exist’,
uponReceiving: ‘a request to get tasks for that project’

404 error interaction
state: ‘project project-id does not exist’,
uponReceiving: ‘a request to get tasks for that project’

Domain validation error interaction
state: ‘a task task-id exists and domain rules exist that prevent its modification’,
uponReceiving: ‘a request to update that task’

Successful interaction where data is created
state: ‘a project with name project-1 does not exist’,
uponReceiving: ‘a request to create a project with name project-1’

Pact spec files and tests setup

The contract tests files should be placed next to the code in the same way we do for the unit tests and use .pact.spec.js as file extension.

...
import { TestBed, getTestBed } from '@angular/core/testing';
import { HttpModule } from '@angular/http';
import * as Pact from 'pact-web';

describe('TasksService', () => {

  let provider;
  let tasksService;

  beforeAll((done) => {
    provider = Pact({ // 1)
      consumer: 'titan-ui2-sample-app',
      provider: 'titan-release-toggling',
      web: true
    });

    // required for slower CI environments
    setTimeout(done, 200);

    // Required if run with `singleRun: false`
    provider.removeInteractions();
  });

  afterAll((done) => {
    provider.finalize().then(done, e => done.fail(e));
  });

  beforeEach(() => {
    TestBed.configureTestingModule({  // 2)
      providers: [TasksService],
      imports: [HttpModule]
    });

    tasksService = getTestBed().get(TasksService);
  });

  afterEach((done) => {
    provider.verify().then(done, e => done.fail(e));
  });

Notes:

  1. Instantiates a mock provider, indicating the name of the consumer app, the name of the provider server and the port of the pact-mock-service.
  2. Injecting the subject under test TasksService. In contract tests we use real HttpModule instead a stub one with MockBackend, it allows HTTP interactions to pass through to the pact mock server.

For any producer we interact, we should configure a karma proxy mapping. This is to redirect requests from our pact tests to the pact-mock-service running on a different port. This allows us to use partial URLs in our app code.

// karma.conf.js
proxies: {
  '/tasks': 'http://localhost:1234/tasks'
},

Running Consumer Contract tests

To run the tests use:
npm test

If the tests pass, the Pact-Mock-Service generates the pact contract files in the projects /pacts folder.

Those pacts, in a CDC testing spirit, can be used now to drive the development of the provider.

Implementing API client code

Client code should be minimal, acting as a facade that simplifies the usage of the provider API to the angular service layer hiding the HTTP details. If your angular service is simple, only dealing with the HTTP calls, you don’t need anything more. In the other hand if you have complex services I recommend to extract that responsibility to a specific Client or Adapter class.

Examples

In the next repository, you can find an example of consumer contract tests for the interactions between a task-list-ui app and a REST API backend.
https://github.com/paucls/task_list_ui-angular/blob/feature/pact-consumer-contract-tests/src/app/tasks-list/tasks.service.pact.spec.ts

To configure an Angular CLI project to support Pact tests check my previous article.

Documentation

https://github.com/pact-foundation/pact-js

Advertisements

Configure Pact JS Consumer Contract tests in Angular

This post describes the steps to configure Karma on an Angular 4+ project to run consumer contract tests using Pact.

1) Install pact and pact-web
yarn add pact –dev
yarn add pact-web –dev

Pact JS is a javascript library to create consumer-driven contract tests.
Pact-web is a module to support Pact in non-Node environments such as Karma.

2) Install Karma Pact
yarn add @pact-foundation/karma-pact –dev

Under the covers karma-pact will be in charge of starting a pact-mock-service in the port localhost:1234 before the tests execution as well as shutting it down when karma finish.

3) Configure Karma to support consumer contract tests
karma.conf.js

frameworks: ['jasmine', '@angular/cli', 'pact'],   // 1) add pact framework
plugins: [
  require('karma-pact'), // 2) add karma-pact plugin
  ...
]
...
pact: [{cors: true, spec: 2, port: 1234, dir: 'pacts/'}],  // 3) configure one or multiple pact-mock-services here
proxies: { // 4) here we can define proxies to redirect requests from our pact tests to the mock server
  '/api/tasks': 'http://localhost:1234/api/tasks'
},

Notice: In some situations, it may be useful to run multiple Pact Mock Servers. For instance, when running tests with karma, if a consumer defines contracts for several providers, the generated Pact contracts may be mixed up. A solution to solve this is to run one Pact Mock Server per provider. Support for multiple services was introduced on karma-pact v1.0.0.

After a successful consumer contract test run, Pact generates contract files and saves them in the /pacts folder.

Run consumer contract tests

At this stage, the project is configured and ready to run contract tests along the rest of unit tests. Tests can be run as usual with
ng test

You can find a working example of this configuration in the next sample app:
https://github.com/paucls/task_list_ui-angular/tree/feature/pact-consumer-contract-tests

In my next article, I explain in more detail how to implement Consumer-Driven Contracts.

Documentation
https://github.com/pact-foundation/pact-js#using-pact-in-non-node-environments
https://github.com/pact-foundation/pact-js/issues/20
https://github.com/pact-foundation/karma-pact
https://github.com/koelec/angular-pact-example
https://github.com/NomiJ/Pact-Angular2-Example

DDD Building Blocks – Value Objects

Value objects are one of the foundational building blocks of Domain-Driven Design. They allow modelling concepts of the Ubiquitous Language that describe, measure or quantify other things in the domain.

It is crucial to understand their characteristics and make more use of them whenever possible instead of trying to model every concept as an Entity or worse, having an Anemic Domain Model where logic related to a value leaks and spreads around services or client code. Values help separating concerns and they are easier to implement, test and maintain.

Examples of Value Objects in a domain could be colour, money, address, e-mail, coordinate, etc. Note that a concept can be an Entity in one domain model and a Value Object in other.

Value Object Characteristics

– Inmutable Conceptual Whole
– Describes, Measures or Quantifies an Entity
– Does not have a unique identity
– Structural equality
– Replaceability
– Side-Effect-Free Behaviour

Implementing Value Objects

Constructors

Value objects are inmutable, do not implement setter methods for its properties, instead provide a constructor with all parameters necessary to initialize its intrinsic state. Parameters may be other objects used to derive internal properties on the new Value.

public class Money {
    private BigDecimal amount;
    private String currency;

    public Money(BigDecimal amount, String currency) {
        this.amount = amount;
        this.currency = currency;
    }

    public BigDecimal getAmount() {
        return amount;
    }

    public String getCurrency() {
        return currency;
    }
...

You can find useful, especially for testing immutability, to provide a copy constructor.

    public Money(Money money) {
        this.amount = money.amount;
        this.currency = money.currency;
    }

Use Guard Clauses in the constructor to ensure that the parameters passed are not only valid individually also as a whole initialize a valid Value.

public class FullName {
    private String firstName;
    private String lastName;

    public FullName(String firstName, String lastName) {
        if (firstName == null || lastName == null) {
            throw new IllegalArgumentException("First and last name are required.");
        }

        this.firstName = firstName;
        this.lastName = lastName;
    }
...

Self-encapsulation/delegation

Value objects must not expose setter methods, but internally, we may use and delegate to them to simplify constructors.

Continue reading “DDD Building Blocks – Value Objects”

One year of Lunch and Learn

Since the beginning of last year, I have the pleasure of facilitating weekly tech lunches in the company I work for. We call them “Lunch and Learn” sessions, and the concept is quite simple. We gather to enjoy our lunch while watching a software conference talk and discuss the topic afterwards.

Ever since we started, the group and the interest has grown significantly amongst developers and management who is supporting the initiative.

I believe these sessions are an excellent way to start building a culture of learning in an engineering group which can lead to more practical activities like lighting talks, coding dojos, readers club, etc.

If all that sounds appealing to you my advice is to:
– Prefer talks on general topics, skills and techniques over a particular language or framework.
– Keep the format open and collaborative, everybody should be able to suggest and vote for next talks.
– Keep an eye on the sessions time, end the video if needed to have always time for the group discussion.

And lastly, sharing here the 43 talks we managed to watch during the year.

Lunch and Learn Playlist 2016

Developing with a Stub backend on Angular 2

In Angular 1 I always use during development a stub or mock REST API backend that allows me running the application without the real API. In this way, the front-end implementation can be started before or in parallel with the backend. It is also useful for small prototypes or demos.

You could quickly implement a fake backend using Node or something similar, but I recommend to mock the backend directly in Angular. It is faster because the application won’t hit the wire and what I really like is that it is stateless (every time the browser is reloaded the stub data is reinitialized).

Those two points are great for local development but what I find fundamental is to use it for running protractor E2E tests, that way I always can rely on a backend substitute that does not keep state between test (protractor reloads the browser before every test, and again the stub data gets reinitialized).

This post shows how to configure a stub backend on Angular 2 using the MockBackend test class.
On develop mode we will override the Http provider to, instead of using a real backend implementation such as XHRBackend, use the MockBackend.

I define the stub backend provider in a separate file stub-backend-provider.ts, here is all the magic happens swapping the backend provider for develop mode and defining stub responses for determined HTTP requests.

src/app/stub-backed/stub-backend-provider.ts

import { Http, BaseRequestOptions, Response, ResponseOptions, RequestMethod, XHRBackend } from '@angular/http';
import { MockBackend, MockConnection } from '@angular/http/testing';

import { environment } from '../../environments/environment';
import { Task } from '../tasks-list/task';
import { generateUuid, getUuidFromUrl } from './stub-backend-utils';

/**
 * Provider to allow the use of a stub backend instead of a real Http service for backend-less development.
 */
export let stubBackendProvider = {
  provide: Http,
  deps: [MockBackend, BaseRequestOptions, XHRBackend],
  useFactory: (mockBackend: MockBackend, options: BaseRequestOptions, realBackend: XHRBackend) => {

    if (!environment.stubBackend) {
      console.log('Configuring real Http backend...');
      return new Http(realBackend, options);
    }

    console.log('Configuring stub Http backend...');

    let tasks: Task[] = [
      {id: '9509c8b4-ad34-4378-b49c-c9206dfd7f75', name: 'Buy milk', done: false, userId: 'user-1'},
      {id: '1b35d8f8-9e80-4316-b3e3-135a8f81200f', name: 'Pay rent', done: true, userId: 'user-1'}];

    mockBackend.connections.subscribe((connection: MockConnection) => {

      // wrap in timeout to simulate server api call
      setTimeout(() => {

        // Get all tasks
        if (connection.request.method === RequestMethod.Get && connection.request.url.match('/tasks$')) {
          connection.mockRespond(new Response(new ResponseOptions({body: tasks.slice()})));
          return;
        }

        // Save task
        if (connection.request.method === RequestMethod.Post && connection.request.url.match('/tasks$')) {
          let newTask = JSON.parse(connection.request.getBody());
          newTask.id = generateUuid();
          tasks.push(newTask);

          connection.mockRespond(new Response(new ResponseOptions({body: newTask})));
          return;
        }

        // Delete task
        if (connection.request.method === RequestMethod.Delete && connection.request.url.match('/tasks/*')) {
          let id = getUuidFromUrl(connection.request.url);
          tasks = tasks.filter(task => task.id !== id);

          connection.mockRespond(new Response(new ResponseOptions()));
          return;
        }
      }, 500);

    });

    return new Http(mockBackend, options);
  }
};

The provider requires uses a new Angular CLI environment property that I called stubBackend, with a value of true on develop and false on production.
/src/environments/environment.ts

export const environment = {
  production: false,
  stubBackend: true
};

/src/environments/environment.prod.ts

export const environment = {
  production: false,
  stubBackend: false
};

Continue reading “Developing with a Stub backend on Angular 2”

Unit test exception messages using JUnit ExpectedException Rule

Traditionally JUnit supported expected exceptions using the @Test annotation like this

@Test(expected = ParsingException.class)
public void parseGame_when_fileIsEmpty_then_throwsParsingException() throws Exception {
   // Arrange
   BufferedReader bufferedReaderMock = mock(BufferedReader.class);
   when(bufferedReaderMock.readLine()).thenReturn("");

   // Act
   gameJsonParser.parseGame(bufferedReaderMock);
}

This has two inconveniences. The expectation should be defined outside the test body and it does not support asserting the exception message.

A workaround commonly used is handling the exception with a try/catch block and assert it manually.

@Test
public void parseGame_when_fileIsEmpty_then_throwsParsingException() throws Exception {
   // Arrange
   BufferedReader bufferedReaderMock = mock(BufferedReader.class);
   when(bufferedReaderMock.readLine()).thenReturn("");

   // Act
   try {
     gameJsonParser.parseGame(bufferedReaderMock);
     fail("Should have thrown ParsingException but did not!");
   }
   catch(ParsingException e) {
     String message = "Parsing failed. Empty game file.";
     assertEquals(message, e.getMessage());
   }
}

As you can see that brings lot of boilerplate code to the test.

Since JUnit 4.7 we can use the ExpectedException core rule to assert exceptions in a more natural flow and also to assert their messages.

ExpectedException: Allows a test to specify expected exception types and messages in the test itself.

@Rule
public ExpectedException exception = ExpectedException.none();

@Test
public void parseGame_when_fileIsEmpty_then_throwsParsingException() throws Exception {
   // Arrange
   BufferedReader bufferedReaderMock = mock(BufferedReader.class);
   when(bufferedReaderMock.readLine()).thenReturn("");

   exception.expect(ParsingException.class);
   exception.expectMessage("Parsing failed. Empty game file.");

   // Act
   gameJsonParser.parseGame(bufferedReaderMock);
}

References:
https://paucls.wordpress.com/2013/11/12/using-junit-rules
https://www.infoq.com/news/2009/07/junit-4.7-rules

Unit test Http Services on Angular 2

Angular 2 testing documentation is more than good, but I believe that it is missing a crucial bit. How should we test services that make use of Http?

For example, let’s say that we want to unit test a TasksService with a getTasks method to GET task resources from a REST API.

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { Task } from './task';

@Injectable()
export class TasksService {
  private tasksUrl = 'http://a-task-list-api.com/tasks';

  constructor(private http: Http) {}

  getTasks(): Promise<Task[]> {
    return this.http.get(this.tasksUrl)
      .toPromise()
      .then(response => response.json() as Task[]);
  }
}

The service uses angular Http service to perform HTTP requests. Unit test of this service should be done in isolation because we don´t want our service to be hitting the real network and at the same time, we want to control and fake the responses from the API. For that reason, we need to use a mock Http service.

Implementing a fake version of the Http service can get really complicated, the good thing is that Angular 2 already provides one for us, the MockBackend.

This is a full example of unit test using MockBackend and BaseRequestOptions to create a Http mock.

import { TestBed, inject, tick, fakeAsync } from '@angular/core/testing';
import { BaseRequestOptions, Http, ConnectionBackend, Response, ResponseOptions, RequestMethod } from '@angular/http';
import { MockBackend } from '@angular/http/testing';

import { TasksService } from './tasks.service';
import { Task } from './task';

describe('TasksService', () => {

  const TASKS: Task[] = [
    {id: 'task-1', name: 'Buy milk', done: false, userId: 'user-1'},
    {id: 'task-2', name: 'Pay rent', done: true, userId: 'user-1'}
  ];

  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        {
          provide: Http,
          deps: [MockBackend, BaseRequestOptions]
          useFactory: (backend: ConnectionBackend, defaultOptions: BaseRequestOptions) => {
          return new Http(backend, defaultOptions);
         }
        },
        {provide: MockBackend, useClass: MockBackend},
        {provide: BaseRequestOptions, useClass: BaseRequestOptions},
        {provide: TasksService, useClass: TasksService}
      ]
    });
  });

  describe('getTasks()', () => {

    it('should return all tasks', inject([TasksService, MockBackend], fakeAsync((tasksService: TasksService, mockBackend: MockBackend) => {
      let result;

      mockBackend.connections.subscribe(c => {
        expect(c.request.method).toBe(RequestMethod.Get);
        expect(c.request.url).toBe('http://paucls-task-list-api.herokuapp.com/tasks');
        let response = new ResponseOptions({body: TASKS});
        c.mockRespond(new Response(response));
      });

      tasksService.getTasks().then(tasks => {
        result = tasks;
      });
      tick();

      expect(result.length).toBe(TASKS.length);
    })));

  });

});

As a reference, in Angular 1 we use the $httpBackend service mock.

    describe('getTasks()', function () {
        it('should return all tasks', function () {
            $httpBackend.expectGET('/tasks').respond(TEST_TASKS);

            let tasks = TasksService.getTasks();
            $httpBackend.flush();
            tasks = resolvePromise(tasks, $q, $scope);

            expect(tasks).toEqual(TEST_TASKS);
        });
    });

Documentation:
https://angular.io/docs/ts/latest/guide/testing.html#!#isolated-service-tests
http://gist.asciidoctor.org/?github-mraible%2Fng2-demo%2F%2FREADME.adoc
https://semaphoreci.com/community/tutorials/testing-angular-2-http-services-with-jasmine