Mocha tests – finish "before" completely, then running "it"s - javascript

I'm running some tests using Mocha 6.1.4 (under WebdriverIO 5.11.6). I want to complete the execution of something in before (read: store some values via an HTTP call) and then start running the tests – the it fellows. Basically, do everythig as if it were synchronous calls. This is (an excerpt of) the (TypeScript) code that I'm using:
// file: ./test/service.spec.ts
describe("#this stuff", () => {
before(() => {
browser.url("login-url");
});
beforeEach(() => {
const service: TheService = new TheService();
return service.setUp(); // ...superagent returns a Promise
});
it("should-do-it", () => {
// ...test + expectations here
});
});
});
// ./src/the-service.ts
import * as superagent from "superagent";
// ...
public setUp() {
return superagent.post(this.uri)
.accept("application/json")
.set("Content-Type", "application/json")
.send(body)
.then();
}
If I understood correctly the Mocha docs (and some questions/answers also here in StackOverflow), returning a Promise inside beforeEach it is all I need to accomplish this, but so far, it doesn't work.
The only consistent way I've found is to execute the content of the it method as a setUp()'s callback:
// ...inside `it`
service.setUp().then(() => {
// ...test + expectations here
});
Can somebody versed on the ECMAScript / TypeScript world shed some light here?

Try this
beforeEach(async () => {
const service: TheService = new TheService();
await service.setUp(); // ...superagent returns a Promise
});

Related

Sinon: Stubbing static method of class does not work as expected

I want to mock the following class, that is used as dependency for another one:
module.exports = class ResponseHandler {
static response(res, status_, data_, codes_) {
console.log('Im here. Unwillingly');
// doing stuff here...
};
The ResponseHandler is imported by the ProfileController and used there:
const response = require('../functions/tools/response.js').response;
module.exports = class ProfileController {
static async activateAccountByVerificationCode(req, res) {
try{
// doing stuff here
return response(res, status.ERROR, null, errorCodes);
}
}
Now I'm writing Unit Tests for the ProfileController and there I'm testing if activateAccountByVerificationCode calls response with the given arguments
describe('ProfileController', () => {
let responseStub;
beforeEach(function() {
responseStub = sinon.stub(ResponseHandler, 'response').callsFake(() => null);
});
But despite the fact that response is mocked, the ProfileController still calls the real implementation of response (See console output: 'Im here. Unwillingly')
it('should respond accordingly if real verification code does not fit with the one passed by the user', async function () {
// here you can still see that real implementation is still called
// because of console output 'I'm here unwillingly'
await controller.activateAccountByVerificationCode(req, res);
console.log(responseStub.called); // -> false
expect(responseStub.calledWith(res, status.ERROR, null, [codes.INVALID_VERIFICATION_CODE])).to.eql(true); // -> negative
});
You need to mock you controllers dependecies with a library like proxyquire first and then use this mocked instance in you test. Otherwise you will still use the original (unstubbed) implementation.
const proxyquire = require('proxyquire');
describe('ProfileController', () => {
let responseStub;
let Controller;
beforeEach(function() {
responseStub = sinon.stub(ResponseHandler, 'response').callsFake(() => null);
Controller = proxyquire('./ProfileController', {'../functions/tools/response':responseStub})
});
it('should respond accordingly if real verification code does not fit with the one passed by the user', async function () {
// here you can still see that real implementation is still called
// because of console output 'I'm here unwillingly'
await Controller.activateAccountByVerificationCode(req, res);
console.log(responseStub.called); // -> false
expect(responseStub.calledWith(res, status.ERROR, null, [codes.INVALID_VERIFICATION_CODE])).to.eql(true); // -> negative
});
Controller then uses your stubbed version of the function and can be inspected.

How would I test this promise based code with jest?

How would I test this code in jest? I'd like to make sure that the error and success of the passed promise is being called as needed. I'm sure it's something sorta simple, but it's driving me crazy. Thanks very much.
handleStatusChangeRequest (changeEntryStatus) {
return changeEntryStatus().then(() => {
this.handleStatusChangeSuccess()
}).catch(err => {
this.handleErrorDisplay(err)
})
}
If your code uses promises, there is a nice way to handle asynchronous tests. Just return a promise from your test, and Jest will wait for that promise to resolve.
If the promise is rejected, the test will automatically fail.
For example, let's say that changeData, instead of using a callback, returns a promise that is supposed to resolve to the string "status has been successfully modified".
Be sure to return the promise - if you omit this return statement, your test will complete before your changeData() -[async function] completes.
Here's a convenient and easy to follow pattern
test('if the data is changed', () => {
return changeData().then((data) => {
expect(data).toBe('status has been successfully modified');
});
})
Happy testing :)
This could be refactored, but for the sake of demonstration, I left the repeating bits in.
In example.spec.js, the callback, changeEntryStatus, is stubbed to return a promise. In order to check if other instance methods (this.method) were called, they are first mocked, then assertions are called on the mock after running the method being tested. Learn more in the Jest docs. (See my thoughts on mocking methods of the unit being tested at the bottom.)
Run the example on repl.it.
example.js:
class Example {
handleStatusChangeRequest(changeEntryStatus) {
return changeEntryStatus().then(() => {
this.handleStatusChangeSuccess()
}).catch(err => {
this.handleErrorDisplay(err)
})
}
handleStatusChangeSuccess() {
console.log('stubbed handleStatusChangeSuccess')
}
handleErrorDisplay(error) {
console.log('stubbed handleErrorDisplay:', error)
}
}
module.exports = Example;
example.spec.js:
const Example = require('./entryStatus')
describe('handleStatusChangeRequest', () => {
it('should run the changeEntryStatus callback', () => {
const {handleStatusChangeRequest} = new Example()
const stub = jest.fn().mockResolvedValue()
handleStatusChangeRequest(stub)
// must return because handleStatusChangeRequest is asynchronous
return expect(stub).toHaveBeenCalled()
});
it('should call example.handleStatusChangeSuccess', async () => {
const example = new Example()
const stub = jest.fn().mockResolvedValue()
example.handleStatusChangeSuccess = jest.fn()
await example.handleStatusChangeRequest(stub)
expect(example.handleStatusChangeSuccess).toHaveBeenCalled();
})
it('should call example.handleErrorDisplay', async () => {
const example = new Example()
const fakeError = { code: 'fake_error_code' }
const stub = jest.fn().mockRejectedValue(fakeError)
example.handleErrorDisplay = jest.fn()
await example.handleStatusChangeRequest(stub)
expect(example.handleErrorDisplay).toHaveBeenCalled()
expect(example.handleErrorDisplay).toHaveBeenCalledWith(fakeError)
});
});
Opinionated Disclaimer: Mocking methods of the unit under test is a smell. Consider checking for the expected effects of calling handleStatusChangeSuccess and handleErrorDisplay instead of checking to see if they were called. Then don't even expose those methods publicly unless consumers of the class need access.
Opinionated Disclaimer: Mocking methods of the unit under test is a
smell. Consider checking for the expected effects of calling
handleStatusChangeSuccess and handleErrorDisplay instead of checking
to see if they were called. Then don't even expose those methods
publicly unless consumers of the class need access.
I wholeheartedly agree with webprojohn's disclaimer. Mocks are a smell as tests should assert the behavior of the code, not its implementation. Testing the latter makes the code brittle to change.
Stepping off my soapbox... :) We're looking for a way to test an asynchronous method. I'm not sure what assertions your tests should make to verify the behavior inside handleStatusChangeSuccess() and handleErrorDisplay(err) so the example below leaves a comment where those assertions would go. The following uses Promise.resolve() and Promise.reject() to trigger the outcomes to test. I've used async/await, Jest has other async examples in their docs.
const Example = require('./example')
describe('handleStatusChangeRequest', () => {
it('should resolve successfully', async () => {
const {handleStatusChangeRequest} = new Example();
const resolvePromise = () => Promise.resolve();
await handleStatusChangeRequest(resolvePromise);
// resolution assertions here
});
it('should resolve errors', async () => {
const {handleStatusChangeRequest} = new Example();
const fakeError = new Error('eep');
const rejectPromise = () => Promise.reject(fakeError);
// if your method doesn't throw, we can remove this try/catch
// block and the fail() polyfill
try {
await example.handleStatusChangeRequest(rejectPromise);
// if we don't throw our test shouldn't get here, so we
// polyfill a fail() method since Jest doesn't give us one.
// See https://github.com/facebook/jest/issues/2129
expect(true).toBe(false);
}
catch (e) {
// rejection assertions here
}
});
});
The answer I have looks so:
**Success tests
const instance = el.find(EntryToolBar).instance()
const spy = jest.spyOn(instance, 'handleStatusChangeSuccess')
await instance.handleStatusChangeRequest(() => Promise.resolve('cool man'))
expect(spy).toHaveBeenCalledTimes(1)
**Error tests
const instance = el.find(EntryToolBar).instance()
const spy = jest.spyOn(instance, 'handleErrorDisplay')
await instance.handleStatusChangeRequest(() => Promise.reject(Error('shit')))
expect(spy).toHaveBeenCalledTimes(1)
As I stated above, the handleStatusChangeSuccess and handleError methods are test else where with some snapshots (they just set state and render out some different jsx). I feel pretty good about this. I'm using spys/mocks, but I'm testing the implementation functions elsewhere. Sufficient?

Randomly failing JS tests

Hi I'm getting this error when running my tests I've read up on the promises and done I'm still unsure where to put it in my test or is it best to do a before each instead to save repetition? Where and what would be the best way to implement the done promise?
Error: Timeout of 2000ms exceeded. For async tests and hooks, ensure
"done()" is called; if returning a Promise, ensure it resolves.
const chakram = require('chakram');
const expect = chakram.expect;
describe("Json assertions", function () {
it("Should return the matching test file and have a 200 response", function () {
let expected = require('../../test/fixtures/testfile.json');
let response = chakram.get("http://test");
expect(response).to.have.json(expected);
expect(response).to.have.status(200);
return chakram.wait();
});
});
I am not familiar with chakram, but generally here is a pattern that should work with testing your promises (using done):
describe('Test Suite', function() {
it('should do something...', function(done) {
const testValue = 'testValue';
someAsyncFunction(testValue).then(function(result) {
result.should.have.property('foo');
result.should.have.property('bar');
done();
});
]);
});
Now for what you have, it looks like the docs for Chakram show how to test with promises (under the Promises header). So your adapted code would be something like:
const chakram = require('chakram');
const expect = chakram.expect;
describe("Json assertions", function () {
it("should...", function () {
let expected = require('../../test/fixtures/testfile.json');
chakram.get("http://test").then(function(response) {
expect(response).to.have.json(expected);
expect(response).to.have.status(200);
});
});
});
Again, I do not know that library, but if your test runner still complains about the done, add done like so:
describe("Json assertions", function () {
it("should...", function (done) {
let expected = require('../../test/fixtures/testfile.json');
chakram.get("http://test").then(function(response) {
expect(response).to.have.json(expected);
expect(response).to.have.status(200);
done();
});
});
});
Edit: done was in describe block, and should have been in it.

Can't test effects of resolved promise

I have the following code in user:
import { redirectTo } from 'urlUtils';
export function create(user) {
api.postUser(user).then((response) => {
redirectTo(response.userUrl);
})
}
And I have the following test:
import * as api from 'api'
import * as user from 'user'
sinon.stub(api, 'postUser').returns(
Promise.resolve({ userUrl: 'a-url' })
);
sinon.spy(urlUtils, 'redirectTo');
const userData = {id: 2};
user.create(userData);
expect(api.postUser.calledWith(userData)).toBe(true); // This passes
expect(urlUtils.redirectTo.calledOnce).toBe(true); // This fails
I have been able to test it on the browser, and the redirect is happening. What am I missing here? I have stubbed the request call to resolve the promise synchronously, so that shouldn't be a problem.
Promises are asynchronous, so when you're doing expect(urlUtils.redirectTo.calledOnce).toBe(true) the promise hasn't been resolved yet.
The easiest way to get around it is to wrap that expectation in a short timeout and then use whatever utility the testing framework you're using provides to signal that an asynchronous test is complete. Something like this:
setTimeout(() => {
expect(urlUtils.redirectTo.calledOnce).toBe(true);
done();
}, 5);
Another, nicer solution is to actually use the promise that your stub returns. First, keep a reference to that promise:
Replace:
sinon.stub(api, 'postUser').returns(
Promise.resolve({ userUrl: 'a-url' })
);
with:
const postUserPromise = Promise.resolve({ userUrl: 'a-url' });
sinon.stub(api, 'postUser').returns(postUserPromise);
then write your expectation like this:
postUserPromise.then(() => {
expect(urlUtils.redirectTo.calledOnce).toBe(true);
done();
});
done() is the function most test frameworks (at least Jasmine and Mocha as far as I know) provide to signal that an asynchronous test is completed. You get it as the first argument to the function your test is defined in and by specifying it in your function signature you're telling the test framework that your test is asynchronous.
Examples:
it("is a synchronous test, completed when test function returns", () => {
expect(true).to.equal(true);
});
it("is an asynchronous test, done() must be called for it to complete", (done) => {
setTimeout(() => {
expect(true).to.equal(true);
done();
}, 5000);
});

$httpBackend doesn't seem to be flushing requests

I am testing my Angular app using ngDescribe. I don't think ngDescribe should be too much of a problem here, as it's just managing dependency injection for me. I first began to attempt my test the way the ngDescribe docs say, in the code below I have changed it to a more direct approach just to see if I could get any changes. I am calling a method that in turn calls $http.post('urlname', data); While debugging I can clearly see that my method gets all the way to where it calls post() but then it never continues. Because of this, my test always times out.
Hopefully I've just got something simple that's wrong! Here is my code. The test that fails is "Should work", all the others pass as expected.
Please also note that this is being processed by babel, both the test and the service, before being tested.
Here is the service, it works perfectly when being used. It has a few other variables involved that I have removed, but I assure you those variables are working correctly. While debugging for the tests, I can see that the await is hit, but it never continues past that, or returns. When used in the app, it returns exactly as expected. I THINK this has something to do with ngmock not returning as it should.
async function apiCall (endPoint, data) {
if (!endPoint) {
return false;
}
try {
return data ? await $http.post(`${endPoint}`, data) : await $http.get(`${endPoint}`);
} catch (error) {
return false;
}
}
Here are the tests:
ngDescribe({
name: 'Api Service, apiCall',
modules: 'api',
inject: ['apiService', '$httpBackend'],
tests (deps) {
let svc;
beforeEach(() => {
svc = deps.apiService;
});
it('is a function', () => {
expect(angular.isFunction(svc.apiCall)).toBe(true);
});
it('returns a promise', () => {
const apiCall = svc.apiCall();
expect(angular.isFunction(apiCall.then)).toBe(true);
});
it('requires an endpoint', async () => {
const apiCall = await svc.apiCall();
expect(apiCall).toBe(false);
});
it('should work', (done) => {
deps.http.expectPOST('fakeForShouldWork').respond({ success: true });
const apiCall = svc.apiCall('fakeForShouldWork', {});
apiCall.then(() => done()).catch(() => done());
deps.http.flush();
});
},
});
The method being called, apiCall, is simply a promise that is resolved by $http.post().then(); It will also resolve false if an error is thrown.
Since deps.http.expectPOST does not fail, I can tell that the outgoing request is sent. I validated this by changing it to expectGET and then I received an error about it being a POST.
I have tried moving the flush() method to all different parts of the test method, but it seems to make no difference.
Any thoughts? Thanks so much for your help!

Categories

Resources