// /test/front-end.js
// Note to future self: write tests.
// Note from future self: thanks, jerk.
Search for a beer
Mark a beer as a "favorite"
Rate a beer
http://pngimg.com/download/2330beer.search = function(query, callback) {
$.ajax({
url: "api/search",
data: { "query": query },
dataType: "json",
success: function searchSuccessHandler(data) {
beer.handleSearchResults(data.results);
callback(data.results);
},
error: function searchErrorHandler() {
beer.notify("Sorry, but the search failed!");
callback(null);
}
});
};
beer.toggleFavorite = function(beerId, callback) {
$.ajax({
url: "api/favorite/" + beerId,
type: "POST",
dataType: "json",
success: function favoriteSuccessHandler(data) {
beer.toggleBeerFavIcon(beerId, data.isFavorite);
callback(data.id, data.isFavorite);
},
// ...
});
};
beer.rateBrew = function(beerId, rating, callback) {
$.ajax({
url: "api/rate/" + beerId,
data: { "rating": rating },
type: "POST",
dataType: "json",
success: function rateSuccessHandler(data) {
beer.showBeerRating(data.id, data.rating, data.avgRating);
callback(data);
},
// ...
});
};
Just like real tests, except they actually pass.
test('Searching - no results', function(asyncDone) {
beerApp.search('fhqwhgads', function(results) {
assertEqual(results, []);
assertEmpty($('#search-results'));
// ...
asyncDone();
});
});
test('Searching - one result', function(asyncDone) {
beerApp.search('foobar', function(results) {
assertNotNull(results);
assertEqual(results.length, 1);
assertEqual(results[0].name, 'Foobar');
// ...
asyncDone();
});
});
test('Searching - HTTP error', function(asyncDone) {
beerApp.search('HTTP-ERROR', function(results) {
assertNull(results);
assertCalled(beerApp.notify);
// ...
asyncDone();
});
});
test('Favorite - good ID, already favorite', function(asyncDone) {
beerApp.toggleFavorite(13, function(id, isFavorite) {
assertEqual(id, 13);
assertEqual(isFavorite, false);
assertCalled(beerApp.toggleBeerFavIcon);
// ...
asyncDone();
});
});
test('Rating - good rating', function(asyncDone) {
beerApp.rateBrew(13, 4, function(data) {
assertEqual(data.id, 13);
assertEqual(data.rating, 4);
assertEqual(data.avgRating, 3.5);
assertCalled(beerApp.showBeerRating);
// ...
asyncDone();
});
});
beforeEach(function() {
setupMockAjax({
url: 'api/search',
method: 'GET',
data: { query: 'fhqwhgads' },
respondWith: { results: [] }
});
});
afterEach(function() {
destroyAllMocks();
});
test('Searching - no results', function(asyncDone) {
// ...
});
Just like real potency, only more idem.
Note: I maintain this one. ;)
beforeEach(function() {
$.mockjax({
url: 'api/search',
data: { query: 'fhqwhgads' },
responseText: { results: [] }
});
});
afterEach(function() {
$.mockjax.clear();
});
$.mockjax({
url: 'api/search',
data: { query: 'HTTP-ERROR' },
status: 500,
responseText: 'Uh oh...'
});
$.mockjax({
url: 'api/search',
method: 'GET',
response: function(req) {
var data = [];
if (req.data.query && req.data.query === 'foobar') {
data.push({
id: 13,
name: 'Foobar',
});
}
this.status = 200;
this.responseText = { results: data };
}
});
$.mockjax({
// ...
response: function(req) {
var data = [];
if (req.data.query && req.data.query === 'foobar') {
// ...
} else if (req.data.query && req.data.query === 'lots') {
for (var i=0; i<100; ++i) {
data.push({ /* ... */ });
}
}
this.status = 200;
this.responseText = { results: data };
}
});
$.mockjax({
url: /api\/favorite\/\d+$/,
method: 'POST',
response: function(settings) {
// ...
}
});
$.mockjax({
url: /api\/favorite\/\d+$/,
method: 'POST',
response: function(settings) {
var id = settings.url.split('/')[2],
data = {
'id': id,
'isFavorite': true
};
this.status = 200;
if (id === 13) {
data.isFavorite = false;
}
this.responseText = data;
}
});
var testApp = angular.module('testApp', ['beerApp', 'ngMockE2E']);
testApp.run(function($httpBackend) {
});
<html ng-app='testApp'>
<head>
...
<script src="angular.js">
<script src="angular-mocks.js">
...
</head>
testApp.run(function($httpBackend) {
$httpBackend.whenGET('/api/search').respond({ results: [] });
});
testApp.run(function($httpBackend) {
$httpBackend.whenGET('/api/search')
.respond(function(method, url) {
var queryParams = customURLToJsonMethod(url);
if (queryParams.query === 'HTTP-ERROR') {
return [500, 'Uh oh...', {}];
}
});
});
testApp.run(function($httpBackend) {
$httpBackend.whenPOST(/api\/rate\/\d+$/)
.respond(function(method, url, data) {
var beerId = url.split('/')[2],
postData = JSON.parse(data);
return [200, {
id: beerId,
rating: postData.rating,
avgRating: 3.5
}, {
'X-Powered-By': 'Mocks'
}];
});
});
describe('Toggling favorites', function() {
beforeEach(function() {
jasmine.Ajax.install();
// ...
});
afterEach(function() {
jasmine.Ajax.uninstall();
});
});
beforeEach(function() {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest('api/rate/13', 'rating=5')
.andReturn({
responseText: JSON.stringify({ id: 13, rating: 5 })
});
// ...
});
beforeEach(function() {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest('api/search?query=fhqwhgads')
.andReturn({
status: 500,
responseText: 'Uh oh...'
});
});
it("should return the correct data", function() {
var cb = jasmine.createSpy("callback");
beerApp.rateBrew(13, 4, cb);
var request = jasmine.Ajax.requests.mostRecent();
var data = {
id: request.url.split('/')[2],
rating: 4,
avgRating: 3.5
};
request.respondWith({
status: 200,
responseText: JSON.stringify(data)
});
expect(cb).toHaveBeenCalledWith(13, 4, 3.5);
});
var server;
beforeEach(function() {
server = sinon.fakeServer.create();
});
afterEach(function() {
server.restore();
});
var server;
beforeEach(function() {
server = sinon.fakeServer.create();
server.autoRespond = true;
server.respondWith(
'GET',
'api/search?',
[
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({ "results": [] })
]
);
});
beforeEach(function() {
// ...
server.respondWith(
'POST',
/api\/rate\/(\d+)/,
function(request, beerId) {
var postData = JSON.parse(request.requestBody);
request.respond(
200,
{ 'Content-Type': 'application/json' },
JSON.stringify({
id: beerId,
rating: postData.rating,
avgRating: 3.75
})
);
}
);
});
$.mockjax({
url: /api\/rate\/\d+$/,
method: 'POST',
response: function(settings, done) {
var mock = this;
someCustomAsyncService(settings, function(data) {
mock.status = 200;
mock.responseText = data;
done();
});
}
});
$.mockjax({
url: 'api/search',
data: { query: 'foobar' },
responseTime: 1200,
headers: {
'X-User-Token': '12345abcde'
},
responseText: { results: [ { id: 13, name: 'Foobar' } ] }
});
$.mockjax({
url: 'api/favorite/1337',
responseTime: 2000,
isTimeout: true
});
$.mockjax({
url: 'api/user/13',
proxy: 'mocks/user-13.json'
});
$.mockjax(function(settings) {
var user = settings.url.match(/api\/user\/(\d+)$/);
if ( user ) {
return {
proxy: 'mocks/user-' + user[1] + '.json'
};
}
// If you get here, there was no url match
});
$.mockjax(function( requestSettings ) {
// Here is our manual URL matching...
if ( /api\/rate\/\d+$/.test(requestSettings.url) ) {
// We have a match, so we return a response callback...
response: function( handlerSettings ) {
if ( handlerSettings.headers['Authentication'] === 'xyz123' ) {
this.responseText = { id: 13, rating: 4, avgRating: 3.5 };
} else {
this.status = 403;
this.responseText = "You are not authorized";
}
}
};
}
// If you get here, there was no url match
});
Test your JavaScript code. ಠ_ಠ
Don't rely on external services (even your own).
Find a library that works for you!
http://chainchomp2.deviantart.com/art/Rapper-Ponies-450348867Jordan Kasper | @jakerella
jordankasper.com/no-server | Example Code