This time I’ll be covering the testing done on the Backend (Mocha) and Frontend (Karma).

Mocha

Mocha is a JS testing framework that allows Test-Driven Development (TDD) and Behavior-Driven Development (BDD) approaches to be used for building the tests.
There’s a lot to learn about Mocha, luckily it’s very well documented. Let’s walk through the examples that are given in MeanJS (inside app/tests). article.server.model.test.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
describe('Article Model Unit Tests:', function() {
	beforeEach(function(done) {
		user = new User({
			firstName: 'Full',
			lastName: 'Name',
			displayName: 'Full Name',
			email: [email protected]',
			username: 'username',
			password: 'password'
		});
		user.save(function() {
			article = new Article({
				title: 'Article Title',
				content: 'Article Content',
				user: user
			});
			done();
		});
	});
	describe('Method Save', function() {
		it('should be able to save without problems', function(done) {
			return article.save(function(err) {
				should.not.exist(err);
				done();
			});
		});
		it('should be able to show an error when try to save without title', function(done) {
			article.title = '';
			return article.save(function(err) {
				should.exist(err);
				done();
			});
		});
	});
	afterEach(function(done) {
		Article.remove().exec();
		User.remove().exec();
		done();
	});
});

The thing I love about Mocha is the readability of the test code, most functions are self explanatory.

The line with beforeEach calls the function that follows, before every test within the current test suite. As you can probably guess, we’re creating a dummy User & Article and connecting the Article to the User. If we skip the test itself and look at afterEach, it’s pretty easy to understand that we’re erasing the Article and User before finishing. Now the Test itself is where it gets interesting:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
describe('Method Save', function() {
	it('should be able to save without problems', function(done) {
		return article.save(function(err) {
			should.not.exist(err);
			done();
		});
	});
	it('should be able to show an error when try to save without title', function(done) {
		article.title = '';
		return article.save(function(err) {
			should.exist(err);
			done();
		});
	});
});

MeanJS is using should.js for the assertions, check out its perfect documentation to know which assertions can be made.
In these 2 tests we’re making sure the Article saves without an error, and in case the title is blank, that it gives an error.

To run the Mocha tests run the command grunt mochaTest.

Let’s move on to Frontend tests, with Karma framework. The tests on Karma are fairly similar to Mocha. Let’s have a look at public/modules/articles/tests/articles.client.controller.test.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
describe('ArticlesController', function() {
	// Initialize global variables

	var ArticlesController,
		scope,
		$httpBackend,
		$stateParams,
		$location;
	// The $resource service augments the response object with methods for updating and deleting the resource.

	// If we were to use the standard toEqual matcher, our tests would fail because the test values would not match

	// the responses exactly. To solve the problem, we define a new toEqualData Jasmine matcher.

	// When the toEqualData matcher compares two objects, it takes only object properties into

	// account and ignores methods.

	beforeEach(function() {
		jasmine.addMatchers({
			toEqualData: function(util, customEqualityTesters) {
				return {
					compare: function(actual, expected) {
						return {
							pass: angular.equals(actual, expected)
						};
					}
				};
			}
		});
	});
	// Then we can start by loading the main application module

	beforeEach(module(ApplicationConfiguration.applicationModuleName));
	// The injector ignores leading and trailing underscores here (i.e. _$httpBackend_).

	// This allows us to inject a service but then attach it to a variable

	// with the same name as the service.

	beforeEach(inject(function($controller, $rootScope, _$location_, _$stateParams_, _$httpBackend_) {
		// Set a new global scope

		scope = $rootScope.$new();
		// Point global variables to injected services

		$stateParams = _$stateParams_;
		$httpBackend = _$httpBackend_;
		$location = _$location_;
		// Initialize the Articles controller.

		ArticlesController = $controller('ArticlesController', {
			$scope: scope
		});
	}));
	it('$scope.update() should update a valid article', inject(function(Articles) {
		// Define a sample article put data

		var sampleArticlePutData = new Articles({
			_id: '525cf20451979dea2c000001',
			title: 'An Article about MEAN',
			content: 'MEAN Rocks!'
		});
		// Mock article in scope

		scope.article = sampleArticlePutData;
		// Set PUT response

		$httpBackend.expectPUT(/articles\/([0-9a-fA-F]{24})$/).respond();
		// Run controller functionality

		scope.update();
		$httpBackend.flush();
		// Test URL location to new object

		expect($location.path()).toBe('/articles/' + sampleArticlePutData._id);
	}));
});

This is a bit more complex because of some AngularJS lingo (which is always a bit more complex). My tip for AngularJS: be patient, read the code slowly to fully understand it.

The first beforeEach function definition (after the var definitions) is there to add the toEqualData function (so we can compare objects properly). The second beforeEach is easy to understand.
The third one injects the extra variables needed, and uses underscores so that local vars can be named the same as the services (purely a convenience/readability thing).

After all this we get to the test itself (starting with it(, just like Mocha). Basically we’re checking the update functionality for Articles by assuming that after the PUT response & update the url is updated to the article’s page.

There’s a lot to learn about Mocha & Karma, and they are quite similar (though I found the Backend tests to be easier to follow, as AngularJS is not involved).

The next article will cover Yeoman generator, and the aspects of generating Models/Controllers/Views.