Angular1 Testing

angular1

Part 2
Part 3
Part 4
Part 5
Part 6

// Angular 1 - Testing - Example 1:

Our tests are not really "unit tests" if they make use of many layers of 
dependencies, and especially if they make server calls.  We need to mock our 
dependencies.  Because our services are classes with all of their dependencies 
injected through their constructors, testing them is simpler.  We just need 
to mock out all of their dependencies and pass them to our service class.

A unit test for our TasksService (app/src/services/tasks/tasks-service.test.js):

import {TaskService} from './tasks-service';
describe('TaskService', () => {
  let _mockServerService;
  let _mockTasks = [{
    owner: 'alice',
    description: 'Build the dog shed.',
    done: true
  }, {
    owner: 'bob',
    description: 'Get the milk.',
    done: false
  }, {
    owner: 'alice',
    description: 'Fix the door handle.',
    done: true
  }];

  beforeEach(() => {
    _mockServerService = {
      get: () => Promise.resolve(_mockTasks)
    };
  });

  it('should get loaded', function() {
    let taskService = new TasksService(_mockServerService);
    chai.expect(tasksService.getTasks()).to.not.be.undefined;
  });

  it('should get tasks', (done) => {

    let tasksService = new TasksService(_mockServerService);

    return tasksService.getTasks()
      .then(tasks => {
        expect(tasks).to.deep.equal(_mockTasks);
        done();
      })
      .then(null, error => { done(error); });
  });
});

The above unit test is based on Mocha.  It:

1. declare a variable _mockServerService
2. define the variable _mockTasks as a hard-coded array of 3 tasks.

The parameter for the the beforeEach function is a function, and before each 
test case is run, this function is run.  This function assigns a JSON object 
to the _mockServerService variable.  This JSON object has one method, named 
'get' and it is a function which will return a promise that always resolve to 
_mockTasks.

The first parameter for the describe method should be the name of the unit that 
we are testing.  The first parameter for the 'it' function should be a string 
that describe the behavior that we are trying to test.  The above test suite 
should output something like:

TaskService
  should get loaded... OK

In the 'should get loaded' test case, we create an instance of TasksService 
passing it our _mockServerService, and then we do an assertion / expect.

In the 'should get tasks' test case, notice that we've specified that our 
function takes a 'done' argument.  This tells Mocha that this is an asynchronous 
test.  An asynchronous test will not be considered successful until done is 
invoked without any arguments.  If we call done() with an argument, the test 
will fail treating that argument as an error.

If an assertion fails, or if an exception is thrown inside a test case, the test 
case will fail.

In case of error, if all we want to do is to pass it on to the done method, we 
do not need to define a new function in the handler.  We can just provide done 
as the handler.  In other words:

.then(null, error => done(error));

is equivalent to:

.then(null, done);

Alternatively, with Mocha, we can return a promise from our test cases:

it('should get tasks', () => {
  let tasksService = new TasksService(_mockServerService);
  return tasksService.getTasks()
    .then(tasks => chai.expect(tasks).to.deep.equal(_mockTasks));
});

A test spy is a function that records arguments, return value, the value of 
the this object, and exception thrown (if any) for all its calls.  A test spy 
can be anonymous function, or it can wrap an existing function.  When using 
Sinon, we'll wrap the existing function with sinon.spy():

...
beforeEach(() => {
  _mockServerService = {
    get: sinon.spy(() => Promise.resolve(_mockTasks))
  };
  _mockServerService.get.reset();
});

When spying on existing functions, the original function will behave as normal, 
but we will be proxied through the spy, which will collect information about 
the calls.  For example, we can check to see if the function has been called:

it('should only call server service once', () => {
  let taskService = new TasksService(_mockServerService);
  return tasksService.getTasks() // call getTasks the first time
    .then(() => tasksService.getTasks())
    .then(() => chai.expect(_mockServerService.get.calledOnce).to.be.true);
});

Notice that the reset function, and the calledOnce property is now available 
on the _mockServerService.get method.
Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License