All:
I am pretty new to Jasmine testing, one question I am wondering about async test is:
Could anybody give a brief explanation how does Jasmine know there is a done() function call inside and waiting for that finish?
For example, if I put some async call in beforeEach:
var flag = false;
beforeEach(function(done){
setTimeout(function(){
// some task here
flag = true;
done();
}, 3000)
})
it("Should be true if the async call has completed", function () {
expect(flag).toEqual(true);
});
How Jasmine know it should let that it spec test wait?
Thanks
Jasmine knows this because you supply the done argument. If you don't have an async call you should omit the done parameter.
beforeEach(function(){
nonAsyncMethod();
})
Related
I have this very simple code:
public async authenticate(username: string, password: string) {
const authenticationResponse = await this.dataProvider.authenticate(username, password);
if (authenticationResponse.result.code == 0) {
//start interval for periodically checking authentication info
this._authInfoIntervalId = setInterval(() => {
this.getAuthInfo();
}, 2000);
In my unit tests only line with this.getAuthInfo() is not concidered as covered. Is it somehow possible to test this? I have tried some approaches with jasmine.createSpy but nothing seemed to work (most likely because I was doing it wrong). Can someone please help me to get it right? Thanks
UPDATE: I tried something like this
it('should test interval somehow', () => {
const intervalCallback = jasmine.createSpy("getAuthInfo");
jasmine.clock().install();
service.authenticate('username', 'password');
jasmine.clock().tick(2001);
expect(intervalCallback).toHaveBeenCalled();
})
and test fails instantly with AuthenticationService should test interval somehow FAILED Expected spy getAuthInfo to have been called.
SOLUTION: I had to spyOn also on dataProvider so I got right response to actually reach that part of code with interval
it('should test interval somehow', async () => {
const intervalCallback = spyOn(service, 'getAuthInfo');
spyOn(dataProvider, 'authenticate').and.returnValue(Promise.resolve(authenticateMockResponse));
jasmine.clock().install();
await service.authenticate('username', 'password');
jasmine.clock().tick(2001);
expect(intervalCallback).toHaveBeenCalled();
});
jasmine.createSpy() should be used for creating a bare spy object, see the documentation. It doesn't have any connection to your service, it's not what you are looking for.
You want to spy a function on an existing object (in your case the service), for which you can use the spyOn function.
it('should test interval somehow', () => {
const intervalCallback = spyOn(service, 'getAuthInfo');
jasmine.clock().install();
service.authenticate('username', 'password');
jasmine.clock().tick(2001);
expect(intervalCallback).toHaveBeenCalled();
})
In Angular, you can use the async and fakeAsync functions from the #angular/core/testing module to properly test statements within a setInterval() function.
First, you'll need to wrap your test in the fakeAsync function, which allows you to use the tick() function to advance the virtual time. Next, you'll need to wrap the code you want to test within the setInterval() function in an async function.
I'm trying to write a test in JavaScript, the method I'm testing makes 2 method calls (model.expandChildren() and view.update();)
// inside 'app' / 'RV.graph'
var expandChildren = function(){
return model.expandChildren().then(function(r) {
view.update(r);
});
};
I've tried to use Jasmine specs to write the test to spyOn both the view and model functions, but it appears you can only have 1 spy in a given test. It seems I'm missing something big here and that there should be a way to mock out multiple methods calls using spies since my function needs to make both of these calls.
I want my spec to be able to run the way it is below, but it currently only passes the first test (the first spy runs as expected), the second test fails because Jasmine is trying to run the actual function, not the spied function:
var model = GRAPH.model;
var view = GRAPH.view;
var app = RV.graph;
describe('#expandChildren', function() {
beforeEach(function() {
// first spy, all good
spyOn(model, 'expandChildren').and.callFake(function() {
return new Promise(function(resolve) {
resolve(testResponse);
});
});
// second spy doesn't work because Jasmine only allows 1
spyOn(view, 'update');
app.expandChildren();
});
// passing test
it('calls model.expandChildren', function() {
expect(model.expandChildren).toHaveBeenCalled();
});
// failing test that runs the REAL view.update method
it('calls view.update', function() {
expect(view.update).toHaveBeenCalled();
});
});
Is there a way to do this with Jasmine?
Remember that you are working with asynchronous calls. The first call is synchronous, so it is recorded, but the second one only happens later. Give yourself some control over when things happen. I commonly use a pattern like this:
describe('#expandChildren', function() {
var resolver;
it('calls model.expandChildren', function(done) {
spyOn(model, 'expandChildren').and.callFake(function() {
return new Promise(function(resolve) {
resolver = resolve;
});
});
spyOn(view, 'update');
app.expandChildren();
expect(model.expandChildren).toHaveBeenCalled();
expect(view.update).not.toHaveBeenCalled();
resolver();
done();
expect(view.update).toHaveBeenCalled();
});
});
This way, the spec will only be run after the promise has been resolved and done() has been called.
Our tests are organized like this:
describe("description", sinon.test(function() {
const harness = this;
it("should do something", function() {
// do something with harness.spy, harness.mock, harness.stub
});
}));
When run, these tests are all failing with TypeError: harness.spy is not a function. I've added some logs and found that harness.spy exists and is a function before the function passed to it is called, but inside the function passed to it, harness.spy is undefined.
Any help understanding what is happening here would be much appreciated.
The problem is the order in which Mocha executes your code. Wrapping the callback to describe with sinon.test cannot work. That's because the callbacks to all describe finish executing before any of the tests in it even start executing. The way sinon.test works, it creates a new sandbox, instruments this with some of the methods of the sandbox (spy, stub, etc.), then calls its callback, and when the callback returns, sinon.test removes from this the methods that it added.
So necessarily, any of the setup performed by sinon.test wrapping a describe callback will be undone before any of the tests are run. Here's an example where I've put some console.log. You'll see both console.log statements execute before any test is run.
const sinon = require("sinon");
describe("description", sinon.test(function() {
const harness = this;
it("should do something", function() {
});
console.log("end of describe");
}));
console.log("outside");
You need to wrap the callbacks you pass to it, instead, like this:
const sinon = require("sinon");
describe("description", function() {
it("should do something", sinon.test(function() {
this.spy();
}));
});
console.log("outside");
If the lifetime of the sandbox created by sinon.test does not work for you, then you have to create your sandbox and clean it "manually", like this:
const sinon = require("sinon");
describe("description", function() {
let sandbox;
before(function () {
sandbox = sinon.sandbox.create();
});
after(function () {
sandbox.restore();
});
it("should do something", function() {
sandbox.spy();
});
it("should do something else", function() {
// This uses the same sandbox as the previous test.
sandbox.spy();
});
});
I want to test some promises with jasmine node. However, the test runs but it says that there are 0 assertions. This is my code, is there something wrong? The then part is successfully called, so if I have a console.log there, it gets called. If I have the code test a http request, on the success, the assertion is correctly interpretated.
describe('Unit tests', function () {
it("contains spec with an expectation", function() {
service.getAllClients().then(function (res) {
expect("hello world").toEqual("hello world");
done();
}).catch(function (err) {
fail();
});
});
});
You need to specify the done argument to the callback you pass to it, so Jasmine knows you are testing something asynchronously:
it("contains spec with an expectation", function(done) {
// ...
When you include that parameter, Jasmine will wait for a while for you to call done so it knows when you're done.
done();
Secondly, in an asynchronous test, it probably is better to fail with a call of done.fail:
done.fail();
I've hit a very weird problem: I'm trying to make unit tests to achieve 100% testing coverage on my application.
And of course I wrote some tests for my controllers but it seems like there is no way to test anything async in Ember (2.4.0) using ember-cli.
I have a function in controller that does this:
readObject() {
this.store.findRecord('myModel',1).then(function(obj) {
this.set('property1',obj.get('property2');
}.bind(this));
}
I'm writing a test that should cover this function.
test('action readObject', function (assert) {
const cont = this.subject();
cont.readObject();
assert.equal(cont.get('property1'), 'someValue);
});
Obivously, this assert wouldn't work because readObject() is async call but this isn't the root of the problem. The problem is that then a callback in this.store.findRecord is being executed - my controller is already destroyed! So I get "calling set on destroyed object" error there.
In other words - even if I wrap my function in a promise and reformat both functions like this:
readObject() {
return new Promise(function(resolve) {
this.store.findRecord('myModel',1).then(function(obj) {
this.set('property1',obj.get('property2');
resolve();
}.bind(this));
}.bind(this));
}
and
test('action readObject', function (assert) {
const cont = this.subject();
cont.readObject().then(function() {
assert.equal(cont.get('property1'), 'someValue);
});
});
It wouldn't work, because after executing readObject() my controllers become immediately destroyed, not waiting for any callbacks.
So, it could be any async call instead of store.findRecord - it could be Ember.run.later, for example.
Does anybody had the same issue? I've read a lot of articles I can't believe that Ember with such a big community doesn't provide a way to make async unit tests.
If anyone has any clues - please give me a hint, cause I'm kinda lost here. At the moment I have two thoughts:
I'm making controllers wrong, Ember doesn't suppose any async operations inside of it. But even if I move async calls to services - I hit the same problem with writing unit tests for them.
I have to decompose my functions to
readObject() {
this.store.findRecord('myModel',1).then(this.actualReadObject.bind(this));
}
actualReadObject(obj) {
this.set('property1',obj.get('property2');
}
to have at least callbacks body covered with tests, but this means I never get 100% testing coverage in my app.
Thank you in advance for any clues. :)
I had a similar problem and looking at the QUnit API - async I solved it. Try out following:
// ... in your controller ...
readObject() {
return this.store.findRecord('myModel',1).then(function(obj) {
this.set('property1', obj.get('property2');
}.bind(this));
}
// ... in your tests, either with assert.async: ...
const done = assert.async(); // asynchronous test due to promises usage
Ember.run(function(){
subject.readObject().then(function(){
// once you reach this point you are sure your promise has been resolved
done();
});
});
// or without assert.async
let promise;
Ember.run(function(){
promise = subject.readObject();
});
return promise;
In a case of unit tests I do also mock other dependencies, for example: store.
this.subject({
property1: null,
store: {
findRecord(modelName, id){
assert.equal(modelName, "myModel1");
assert.equal(id, 1);
return new Ember.RSVP.Promise(function(resolve){
resolve(Ember.Object.create({ property2: "a simple or complex mock" }));
})
}
}
});
I am not sure about the second case (the one without assert.async). I think it would work too, because the test suite returns a promise. This gets recorgnized by QUnit that waits for the promise.
I copy my own solution here, cause code formatting in comments isn't too good.
test('async', function (assert) {
const cont = this.subject();
const myModel = cont.get('store').createRecord('myModel'); //
// Make store.findRecord sync
cont.set('store',{
findRecord(){
return { then : function(callback) { callback(myModel); } }
}
});
// Sync tests
assert.equal(cont.get('property2'), undefined);
cont.readObject(); // This line calls store.findRecord underneath
assert.equal(cont.get('property2'), true);
});
So, I just turned store.findRecord into a sync function and it runs perfect. And again - many thanks to Pavol for a clue. :)