A mock API service

To begin testing the mock API service, let's create a new services folder and add a mockSpeakerService.spec.js file.

Inside that file, we need to import our assertion library, create our initial describe, and write an existence test.

import { expect } from 'chai';

describe('Mock Speaker Service', () => {
it('exits', () => {
expect(MockSpeakerService).to.exist;
});
});

Start the npm test script with watch. The test we just wrote should fail. To make the test pass, we must create a MockSpeakerService object. Let's play devil's advocate a little and create an object in this file, but only enough of an object to make the test pass.

let MockSpeakerService = {};

This line passes the currently failing test, but clearly isn't what we are after. It does, however, force us to write more robust tests. The next test we can write is one that proves that the MockSpeakerService can be constructed. This test should ensure that we have defined the MockSpeakerService as a class.

it('can be constructed', () => {
// arrange
let service = new MockSpeakerService();

// assert
expect(service).to.be.an.instanceof(MockSpeakerService);
});

This test fails, stating that MockSpeakerService is not a constructor. The way to fix this is to change MockSpeakerService into a class.

class MockSpeakerService {
}

Now that we have a class that can be instantiated, the next test we write can start to test actual functionality. So, what functionality are we going to test? Looking at the requirements, we can see that the first scenario involves requesting all the speakers and receiving no speakers. That's a reasonably simple scenario to test. What would we call the function in the MockSpeakerService that would get all the speakers? Because we are trying to get all the speakers, a simple name that would not be redundant and fits the repository pattern we discussed in the C# backend is simply getAll. Let's create a nested describe and an existence test for a getAll class method.

describe('Get All', () => {
it('exists', () => {
// arrange
let service = new MockSpeakerService();

// assert
expect(service.getAll).to.exist;
});
});

As per usual, this test should fail and it should fail with expected undefined to exist. Making this test pass is relatively simple, just add a getAll method to the MockSpeakerService class.

class MockSpeakerService {
getAll() {
}
}

The next thing we need to decide is the result we should expect when there are no speakers. Looking back at the backend, we should be receiving an empty array when no speakers are present. Looking at the requirements, the system should present a NO_SPEAKERS_AVAILABLE message. Should the service be responsible for displaying that message? In this case, the answer is no. The react component should be responsible for displaying the NO_SPEAKERS_AVAILABLE message when we get to that portion of the code. For now, we should expect, when no speakers exist, to receive an empty data set.

Because we are extending the context of the test, let's create another describe for that context extension.

describe('No Speakers Exist', () => {
it('returns an empty array', () => {
// arrange
let service = new MockSpeakerService();

// act
let promise = service.getAll();

// assert
return promise.then((result) => {
expect(result).to.have.lengthOf(0);
});
});
});

Notice the syntax we used for this test. We return the promise and make our assertions inside the then function. This is because we want our test to operate on asynchronous code from our service. The majority of backend operations will need to be asynchronous and one convention for dealing with that asynchronicity is to use promises. Asynchronous tests, that is, tests dealing with promises, in Mocha require that the promise is returned from the test so that Mocha can know to wait for the promise to resolve before closing out the test.

And now, to make the test pass, all we need to do is return a promise that resolves with an empty array from the getAll method. We are going to use a zero delay setTimeout here which will set us up to implement some kind of delay for development purposes later on. The reason we want a delay is so that we can test the operation of the UI in the event of a slow network response.

getAll() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Object.assign([], this._speakers));
}, 0);
});
}

Now we have the first scenario passing and enough code to warrant a refactoring. We are declaring the service variable in multiple places and we don't have a context that represents a baseline instantiation of that variable. Let's create a describe to wrap all the post instantiation tests and add a beforeEach to initialize a service variable scoped to that describe and available to all the tests within it.

Here are the tests after the refactoring:

describe('Mock Speaker Service', () => {
it('exits', () => {
expect(MockSpeakerService).to.exist;
});

it('can be constructed', () => {
// arrange
let service = new MockSpeakerService();

// assert
expect(service).to.be.an.instanceof(MockSpeakerService);
});

describe('After Initialization', () => {
let service = null;

beforeEach(() => {
service = new MockSpeakerService();
});

describe('Get All', () => {
it('exists', () => {
// assert
expect(service.getAll).to.exist;
});

describe('No Speakers Exist', () => {
it('returns an empty array', () => {
// act
let promise = service.getAll();

// assert
return promise.then((result) => {
expect(result).to.have.lengthOf(0);
});
});
});
});
});
});

The next scenario, the speaker listing, is for when speakers do exist. The first test for this scenario will need to add at least one speaker to the mock API. Let's create a new describe inside GetAll but separate from No Speakers Exist.

describe('Speaker Listing', () => {
it('returns speakers', () => {
// arrange
service.create({});

// act
let promise = service.getAll();

// assert
return promise.then((result) => {
expect(result).to.have.lengthOf(1);
});
});
});

We have added, as part of the setup for this test, a reference to a Create method. This method does not yet exist and our test can't pass without it. So, we need to temporarily ignore this test and write tests for Create. We can ignore this test by skipping it.

it.skip('returns speakers', () => {

Now, we can write a new describe block inside the After Initialization block for Create.

describe('Create', () => {
it('exists', () => {
expect(service.create).to.exist;
});
});

And to make the test pass we add the Create method to the mock service class.

class MockSpeakerService {
create() {

}

We could, from this point, write a few tests to add validation logic to the Create method. However, we don't currently have any scenarios that reference a Create method on the API. Since this method exists only for testing purposes, we are going to leave it alone with just an exists test. Let's move back to our scenario test.

Now that Create exists, we should receive the failure that the test is expecting, which is that we expected a length of 1 but instead we have a length of 0. Remove skip from the test and verify.

To make this test pass, we essentially have to implement the basic logic to create and make a modification to getAll.

class MockSpeakerService {
constructor() {
this._speakers = [];
}

create(speaker) {
this._speakers.push(speaker);
}

getAll() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(Object.assign([], this._speakers));
}, 0);
});
}
}

We can consider the current tests sufficient to move forward and start testing our data flow.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.225.254.192