Last active
June 25, 2018 14:17
-
-
Save fragsalat/0a33295f0340e7e047ef5a820d8d2245 to your computer and use it in GitHub Desktop.
This is an example mock for aurelia-http-client. It provides the ability to expect outgoing calls and emulate the response to test the correct behavior of an function. The expectation can validate the URL, request method, request headers and request body.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {HttpClientMock} from 'test/unit/mocks/http/http-client-mock'; | |
import {reportUnfulfilled, reportUnexpected} from 'test/unit/mocks/http/mock-helper'; | |
import {SomeViewModel} from 'app/view/some-view'; | |
describe('A ViewModel', function() { | |
/** @type Element */ | |
let host; | |
/** @type HttpClientMock */ | |
let http; | |
/** @type SomeViewModel */ | |
let viewModel; | |
beforeEach(function() { | |
host = document.createElement('div'); | |
viewModel = new SomeViewModel(host, {tr: () => ''}); | |
http = new HttpClientMock(); | |
http.registerGlobally(); | |
}); | |
afterEach(function() { | |
reportUnfulfilled(expect, http); | |
reportUnexpected(expect, http); | |
}); | |
it('calls the backend', function(done) { | |
viewModel.someData = [1,2,3]; | |
// Validate the request goes with correct method, headers and body out | |
// and emulate the response with code 201 and Location header | |
http.expect('/some-backend-service/resource') | |
.withMethod('POST') | |
.withRequestHeader('Content-Type', 'application/json') | |
.withRequestBody(viewModel.someData) | |
.withResponseStatus(201) | |
.withResponseHeader('Location', 'http://result/url'); | |
viewModel.createSomething(new Event('click')).then(done); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Expectation} from 'app/test/mocks/http/expectation'; | |
export class ExpectationBuilder { | |
/** | |
* @param handler {RequestHandler} | |
*/ | |
constructor(handler) { | |
this.expectation = new Expectation(); | |
// Set reference to handler | |
handler.expect(this.expectation); | |
} | |
/** | |
* Expect an url to be called | |
* @param url {String} | |
* @returns {ExpectationBuilder} | |
*/ | |
withUrl(url) { | |
this.expectation.url = url; | |
return this; | |
} | |
/** | |
* Expect the request to be done with a specific method | |
* @param method {String} | |
* @returns {ExpectationBuilder} | |
*/ | |
withMethod(method) { | |
this.expectation.method = method; | |
return this; | |
} | |
/** | |
* Expect the request to contain certain header | |
* @param name {String} | |
* @param value {String} | |
* @returns {ExpectationBuilder} | |
*/ | |
withRequestHeader(name, value) { | |
this.expectation.requestHeaders[name.toLocaleLowerCase()] = value; | |
return this; | |
} | |
/** | |
* Expect the request to send specific data | |
* @param body {String} | |
* @returns {ExpectationBuilder} | |
*/ | |
withRequestBody(body) { | |
this.expectation.requestBody = typeof body === 'string' ? body : json(body); | |
return this; | |
} | |
/** | |
* Expect the request to respond with specific http status code | |
* @param status {Number} | |
* @returns {ExpectationBuilder} | |
*/ | |
withResponseStatus(status) { | |
this.expectation.responseStatus = status; | |
return this; | |
} | |
/** | |
* Expect the response to contain a certain header | |
* @param name {String} | |
* @param value {String} | |
* @returns {ExpectationBuilder} | |
*/ | |
withResponseHeader(name, value) { | |
this.expectation.responseHeaders[name.toLocaleLowerCase()] = value; | |
return this; | |
} | |
/** | |
* Expect the response to contain specific data | |
* @param body {String|Object} | |
* @returns {ExpectationBuilder} | |
*/ | |
withResponseBody(body) { | |
this.expectation.responseBody = typeof body === 'string' ? body : json(body); | |
return this; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export class Expectation { | |
url = null; | |
method = null; | |
requestHeaders = {}; | |
requestBody = null; | |
responseStatus = 200; | |
responseHeaders = {}; | |
responseBody = ''; | |
/** | |
* @param xhr {XHRMock} | |
* @returns {Boolean} | |
*/ | |
matches(xhr) { | |
if (this.url && xhr.url !== this.url) { | |
return false; | |
} | |
if (this.method && xhr.method !== this.method) { | |
return false; | |
} | |
if (!this._compareObjects(this.requestHeaders, xhr.requestHeaders)) { | |
return false; | |
} | |
return !(this.requestBody && xhr.requestText !== this.requestBody); | |
} | |
/** | |
* @param object1 {Object} | |
* @param object2 {Object} | |
* @returns {Boolean} | |
* @private | |
*/ | |
_compareObjects(object1, object2) { | |
if (Object.keys(object1).length) { | |
for (let key in object1) { | |
if (!object1.hasOwnProperty(key)) { | |
continue; | |
} | |
if (!object2[key] || object2[key] !== object1[key]) { | |
return false; | |
} | |
} | |
} | |
return true; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {Container} from 'aurelia-dependency-injection'; | |
import * as http from 'aurelia-http-client'; | |
import {request as XHRMock} from 'mock-xhr'; | |
function createMockMessageProcessor(xhr) { | |
return new http.RequestMessageProcessor(xhr, [ | |
http.timeoutTransformer, | |
http.credentialsTransformer, | |
http.progressTransformer, | |
http.responseTypeTransformer, | |
http.headerTransformer, | |
http.contentTransformer | |
]); | |
} | |
export class HttpClientMock extends http.HttpClient { | |
/** | |
* @constructor | |
*/ | |
constructor() { | |
super(); | |
let handler = this.handler = new RequestHandler(); | |
// Ensure each http client has it's own xhr mock instance with it's own handler | |
function XHR() { | |
XHRMock.apply(this, arguments); | |
this.upload = {}; | |
this.onsend = () => handler.handle(this); | |
} | |
XHR.prototype = XHRMock.prototype; | |
// Create message processor with xhr mock | |
this.requestProcessorFactories = new Map(); | |
this.requestProcessorFactories.set(http.HttpRequestMessage, () => createMockMessageProcessor(XHR)); | |
this.requestProcessorFactories.set(http.JSONPRequestMessage, () => createMockMessageProcessor(XHR)); | |
} | |
/** | |
* @void | |
*/ | |
registerGlobally() { | |
new Container().makeGlobal(); | |
Container.instance.registerInstance(http.HttpClient, this); | |
} | |
/** | |
* @param url {String} | |
* @returns {ExpectationBuilder} | |
*/ | |
expect(url) { | |
return new ExpectationBuilder(this.handler).withUrl(url); | |
} | |
/** | |
* @returns {Boolean} | |
*/ | |
isDone() { | |
return this.handler.isDone(); | |
} | |
/** | |
* @returns {Array<Expectation>} | |
*/ | |
getExpected() { | |
return this.handler.expected; | |
} | |
/** | |
* @returns {Boolean} | |
*/ | |
hadUnexpected() { | |
return this.handler.hadUnexpected(); | |
} | |
/** | |
* @returns {Array<XHRMock>} | |
*/ | |
getUnexpected() { | |
return this.handler.unexpected; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function json(obj) { | |
return JSON.stringify(obj); | |
} | |
export function reportUnfulfilled(expect, httpMock) { | |
let expectedUrls = httpMock.getExpected().map(stringifyXhr); | |
expect(httpMock.isDone()).toBeTruthy('Never called urls: \n' + expectedUrls.join('\n')); | |
} | |
export function reportUnexpected(expect, httpMock) { | |
let unexpectedUrls = httpMock.getUnexpected().map(stringifyXhr); | |
expect(httpMock.hadUnexpected()).toBeFalsy('Unexpected calls: \n' + unexpectedUrls.join('\n')); | |
} | |
function stringifyXhr(xhr) { | |
return `${xhr.method}: ${xhr.url} | |
Sent ${stringifyHeaders(xhr.requestHeaders)} | |
${xhr.requestText || xhr.requestBody} | |
Received ${stringifyHeaders(xhr.responseHeaders)} | |
${xhr.responseText || xhr.responseBody}`; | |
} | |
function stringifyHeaders(headers) { | |
let keys = Object.keys(headers); | |
let headerRepresentations = keys.map(key => `${key}: ${headers[key]}`); | |
return keys.length ? `Headers ${headerRepresentations.join(' | ')}` : ''; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class RequestHandler { | |
/** | |
* @type {Array<Expectation>} | |
*/ | |
expected = []; | |
/** | |
* @type {Array<XHRMock>} | |
*/ | |
unexpected = []; | |
/** | |
* @param expected {Expectation} | |
*/ | |
expect(expected) { | |
this.expected.push(expected); | |
} | |
/** | |
* @param xhr {XHRMock} | |
*/ | |
handle(xhr) { | |
for (let i = this.expected.length; i--;) { | |
let expected = this.expected[i]; | |
// Compare url and http method | |
if (expected.matches(xhr)) { | |
// Set expected response headers on xhr mock | |
xhr.responseHeaders = expected.responseHeaders || {}; | |
// Emulate response with expected values | |
xhr.receive(expected.responseStatus, expected.responseBody); | |
// Remove the expected request because we handle it only once | |
this.expected.splice(i, 1); | |
return; | |
} | |
} | |
// No expected request was found if the function hasn't returned yet | |
this.unexpected.push(xhr); | |
} | |
/** | |
* @returns {Boolean} | |
*/ | |
isDone() { | |
return !this.expected.length; | |
} | |
/** | |
* @returns {Boolean} | |
*/ | |
hadUnexpected() { | |
return !!this.unexpected.length; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment