I am writing tests for a REST client library which has to "login" against the service using the OAuth exchange. In order to prevent logging in for every endpoint I am going to test I'd like to write some sort of "test setup" but I am not sure how I am supposed to do this.
My test project structure:
test
endpoint-category1.spec.ts
endpoint-category2.spec.ts
If I had only one "endpoint category" I had something like this:
describe('Endpoint category 1', () => {
let api: Client = null;
before(() => {
api = new Client(credentials);
});
it('should successfully login using the test credentials', async () => {
await api.login();
});
it('should return xyz\'s profile', async () => {
const r: Lookup = await api.lookup('xyz');
expect(r).to.be.an('object');
});
});
My Question:
Since the login() method is the first test there, it would work and the client instance is available for all the following tests as well. However, how can I do some sort of setup where I make the "logged in api instance" available to my other test files?
Common code should be moved to beforeEach:
beforeEach(async () => {
await api.login();
});
At this point should successfully login using the test credentials doesn't make much sense because it doesn't assert anything.
describe('Endpoint category 1', () => {
let api: Client = null;
beforeEach(() => {
api = new Client(credentials);
});
afterEach(() => {
// You should make every single test to be ran in a clean environment.
// So do some jobs here, to clean all data created by previous tests.
});
it('should successfully login using the test credentials', async () => {
const ret = await api.login();
// Do some assert for `ret`.
});
context('the other tests', () => {
beforeEach(() => api.login());
it('should return xyz\'s profile', async () => {
const r: Lookup = await api.lookup('xyz');
expect(r).to.be.an('object');
});
});
});
Have you had a look at https://mochajs.org/#asynchronous-code ?
You can put in a done-parameter in your test functions and you will get a callback with this you have to call.
done() or done(error/exception)
This done would be also available in before and after.
When calling done() mocha knows your async-code has finished.
Ah. And if you want to test for login, you shouldn't provide this connection to other tests, because there is no guarantee of test order in default configuration.
Just test for login and logout afterwards.
If you need more tests with "login-session", describe a new one with befores.
Related
I would like to run code before and after all the tests, not file wide, test wide. For example;
Before starting the e2e tests, I would like to run the server in test mode with the database. After all, I would like to flush my db and close the processes.
I don't know if it is possible but I also would like to have a global db variable to do tests. What I am currently doing is like this:
describe("Posts Module", () => {
let dbService: DatabaseService;
beforeAll(async () => {
dbService = new DatabaseService();
await dbService.init();
});
it("should give the posts", () => {
supertest(app)
.get("/posts")
.expect(200)
.then(async (response) => {
const dbPosts = await dbService.getPosts();
expect(response.body).toBeDefined();
expect(response.body.posts).toEqual(dbPosts);
});
});
afterAll(async () => {
await flushDb(dbService);
await dbService.close();
});
});
But what I actually want is initing this database service only once before all of the module tests (also starting the server, currently I start the server manually and run the tests afterwards).
I have an application where I have a Controller called NotesController and a service injected in it called NotesService which I use to retrieve notes.
I am trying to write a unit test for my Controller using spies from Chai library:
chai.use(spies);
describe('Notes Controller', () => {
chai.spy.on(notesController, 'notesService.get', (id) => id);
}
describe('Get note by id', () => {
it('Retrieves note successfully', (done) => {
notesController.get("1")
.then(() => {
chai.expect('notesService.get').to.have.been.called();
})
.catch((error: any) => {
done(error)
});
}
}
My test fails because for some reason it tries to run that method notesService.get, so it fails when it tries to connect to the DB.
How can I mock this method call (notesService.get) using spies instead of actually running it?
I am new to the javascript ecosystem, and I am trying to wrap my head around a decent way to do some basic unit testing against the REST API using a stored set of fixtures, i.e. .json files.
My entry point sets up a testing environment by connecting to a test database and emits a ready state.
if (!process.env.TESTING) {
/* Connect to MongoDB */
connect(process.env.DEV_DB_HOST, process.env.DEV_DB, () => app.emit(ready));
app.on(ready, () => {
app.listen(port, () => console.log(`Listening on port ${port}!`));
});
} else {
connect(process.env.TEST_DB_HOST, process.env.TEST_DB, () => app.emit(ready));
}
export default app;
Once the connection is established, the collection recreated in the test database using fixtures and the tests are executed.
import Paper from '../models/paper.js';
describe('Home Page', () => {
before(async() => {
await new Promise((resolve, reject) => {
app.on(ready, () => resolve());
}).then(async() => {
try {
await Paper.collection.drop();
} catch(error) {
/* Mongo throws an exception if the collection in question does not exist */
}
await Paper.create(fixtures);
});
});
after(async() => {
await Paper.collection.drop();
});
it('Returns a list of items ordered by date in descending order', async() => {
const response = await chai.request(app).get('/');
expect(response.body).to.have.lengthOf(2);
expect(response.body).to.be.sortedBy("date", { descending: true });
});
});
If I replace the before and after hooks with beforeEach and afterEach, since the tests are async, is it possible that the hook gets executed for a test and dumps out the collection before another test, which is using the collection, finishes executing? If so, what is a good way to go about populating a test database with fixtures for each test case?
Using nock, is there a way to disable a single nock scope?
I've been struggling with some tests that set up nocks of the same URL as some other tests. They both run fine separately, but when run in the same mocha session one of them fails, because I'm unable to re-nock the active nock scopes, meaning the nocks that were set up catches all the requests.
What I've tried:
If I set up some nocks in before() and then call scope.persist(false) in my after(), it only "unpersists" the scope, so that it's active for one more request. It does not immediately disable it.
I've found that nock.cleanAll() immediately disables the nocks so that they can be set up again, but then it also disables any global nocks that may have been set up once, common to all test cases.
So far, the only solutions I've found are 1) use unique URL:s for all nocks, which isn't always possible or 2) use nock.cleanAll() and don't rely on any global nocks - instead make sure to only set up nocks in local before() functions, including setting up the global ones repeatedly for every test that needs them.
It seems it would be highly useful to be able to do
scope = nock('http://somewhere.com').persist().get('/'.reply(200, 'foo');
and then use that nock in a bunch of tests, and finally do
scope.remove();
However, I've not been able to do something like this. Is it possible?
Example:
before(async () => {
nock('http://common').persist().get('/').reply(200, 'common');
});
after(async () => {
});
describe('Foo tests', () => {
let scope;
before(async () => {
scope = nock('http://mocked').persist().get('/').reply(200, 'foo');
});
after(() => {
// scope.persist(false); // This causes the Bar tests to use the Foo nocks one more time :(
// nock.cleanAll(); // This also disables the common nocks
});
it('Should get FOO', async () => {
expect(await fetch('http://mocked').then(res => res.text())).to.equal('foo');
expect(await fetch('http://common').then(res => res.text())).to.equal('common');
});
it('Should get FOO again', async () => {
expect(await fetch('http://mocked').then(res => res.text())).to.equal('foo');
expect(await fetch('http://common').then(res => res.text())).to.equal('common');
});
});
describe('Bar tests', () => {
let scope;
before(async () => {
scope = nock('http://mocked').persist().get('/').reply(200, 'bar');
});
after(() => {
// scope.persist(false);
// nock.cleanAll();
});
it('Should get BAR', async () => {
expect(await fetch('http://mocked').then(res => res.text())).to.equal('bar');
expect(await fetch('http://common').then(res => res.text())).to.equal('common');
});
it('Should get BAR again', async () => {
expect(await fetch('http://mocked').then(res => res.text())).to.equal('bar');
expect(await fetch('http://common').then(res => res.text())).to.equal('common');
});
});
These tests either fail the 3rd test if using scope.persist(false) (since that test still gets the foo version), or fails tests 3 and 4 if using nock.cleanAll(), since the common nocks are then removed.
I also had this issue and found a way to work around it by listening to the request event emitted by the scope and removing the interceptor when the event is fired. Ideally, I think you should be listening to the replied event but for some reason, that event wasn't firing when I tried it, not sure why. But the code below worked for me:
/**
* #jest-environment node
*/
const nock = require('nock');
describe('Test suite', () => {
test('Test case', async () => {
let interceptor1 = nock('https://example-url.com', {
reqHeaders: {
'Content-Type': 'text/xml',
soapaction: 'http://www.sample.com/servie/getOrders',
},
})
.post('/');
let interceptor2 = nock('https://example-url.com', {
reqHeaders: {
soapaction: 'http://www.sample.com/servie/getProducts',
},
})
.post('/');
let scope = interceptor1.replyWithFile(200, path.join(__dirname, './path1.xml'));
interceptor2.replyWithFile(200, path.join(__dirname, './path.xml'));
scope.on('request', (req, interceptor) => {
nock.removeInterceptor(interceptor1);
});
const resp = await asynccall();
expect(resp).toStrictEqual(exp);
});
});
As described here: Unable to remove interceptors using nock I found a way of not storing the interceptors by just setting the mock again (which apparently returns the interceptor again) and then using the returned interceptor in the removeInterceptor() function. This returns true indeed and does work in my tests.
I found a pretty simple workaround for this one - the scope has a property called "interceptors", which is an array of the various interceptors the scope uses. You can replace the "body" property of the interceptor with whatever you want.
let scope = nock(base).get(path).reply(200, []).persist();
testable.call(path).then(console.log); //returns []
scope.interceptors[0].body = [1,2,3];
testable.call(path).then(console.log); //returns [1,2,3]
// Balance.jsx
...
updateToken () {
const parseResponse = (response) => {
if (response.ok) {
return response.json()
} else {
throw new Error('Could not retrieve access token.')
}
}
const update = (data) => {
if (data.token) {
this.data.accessTokenData = data
} else {
throw new Error('Invalid response from token api')
}
}
if (this.props.balanceEndpoint !== null) {
return fetch(this.props.accessTokenEndpoint, {
method: 'get',
credentials: 'include'
})
.then(parseResponse)
.then(update)
.catch((err) => Promise.reject(err))
}
}
componentDidMount () {
this.updateToken()
.then(() => this.updateBalance())
}
}
// Test
it('updates the balance', () => {
subject = mount(<Balance {...props} />)
expect(fetchMock.called('balance.json')).to.be.true
})
I can't figure out how to test the above using Mocha. The code is does work the method updateBalance is called and the fetch api call actually does happen, but the test still fails. If I call updateBalance() synchronously it passes... How do I tell the test to wait for the promise to resolve?
You don't really say what you want to test that the
method does, but if all you want to test is that the method resolves on a network call, then there is no need for Sinon or any of that, as this is all you need:
describe("BalanceComponent", () => {
it("should resolve the promise on a successful network call", () => {
const component = new BalanceComponent({any: 'props', foo: 'bar'});
// assumes you call a network service that returns a
// successful response of course ...
return component.updateToken();
});
});
This will test that the method actually works, but it is slow and is not a true unit test, as it relies on the network being there and that you run the tests in a browser that can supply you with a working implementation of fetch. It will fail as soon as you run it in Node or if the service is down.
If you want to test that the method actually does something specific, then you would need to to that in a function passed to then in your test:
it("should change the token on a successful network call", () => {
const component = new BalanceComponent({any: 'props', foo: 'bar'});
const oldToken = component.data.accessTokenData;
return component.updateToken().then( ()=> {
assert(oldToken !== component.data.accessTokenData);
});
});
If you want to learn how to test code like this without being reliant on there being a functioning link to the networked service you are calling, you can check out the three different techniques described in this answer.