How to ensure that jasmine's expect() was executed in test - javascript

I have such test for angular app:
it("should return false if all products loaded", function () {
$httpBackend.flush();
scope.loadNextProducts(15).then(function (isThereMoreToLoad) {
expect(isThereMoreToLoad).toBe(false);
});
scope.$apply();
});
If i forget to write ether $httpBackend.flush(); or scope.$apply(); test will never reach expect() part and test will be successful.
Is there way to ensure that jasmine test executed expect(), and if not then it should fail?
Something like specifying to it() how many expect() to expect, or tell jasmine that each test should execute at least one expect() otherwise it should fail.

Try this:
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
That should, well, verify that your tests have no expectations that have not been visited and it should also verify that all of your expected server calls have been called.
You can find more info on the Angular $backend docs

Related

Jasmine Node javascript promise testing

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();

Inject real implementation of Angular's $timeout service in a Karma/Jasmine unit test

I have a small Angular service that handles asynchronous rate-limiting of other functions, similar to this example. Since the main purpose of this class is to moderate asynchronous behaviors, I'd like to be able to test this service asynchronously - I won't be able to prove that this class is working with a purely synchronous test.
If I understand correctly, when Angular's ngMock module is loaded, the built-in $timeout service is replaced with a mocked version of $timeout which allows tests to synchronously run functions that are ordinarily asynchronous. However, in this case, I'd like to use the real implementation of $timeout instead of the mocked version.
How can I inject the real implementation of $timeout into my unit test?
Here's what my tests looks like currently (I'm writing my tests in TypeScript):
describe('My Tests', () => {
let myService: MyService,
$timeout: ng.ITimeoutService;
beforeEach(() => {
inject(($injector) => {
// this gets me the mocked version of $timeout
$timeout = $injector.get('$timeout');
});
myService = new MyService($timeout);
jasmine.DEFAULT_TIMEOUT_INTERVAL = 5000;
});
it('should pass', (done) => {
$timeout(50)
.then(() => {
// this is never called
expect(1).toBe(1);
})
.finally(done);
});
});
When I run this test, Karma complains that the test too took long because the mocked$timeout service never actually kicks off its deferred timeout:
Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
You need to call $timeout.flush(); to force all $timeout inside your controller to be released :
it('a ctrl with $timeout inside', inject(function($timeout) {
var myCOntroller = $controller('Controller', { $scope: $scope });
// flush timeout(s) for all code under test.
$timeout.flush();
// this will throw an exception if there are any pending timeouts.
$timeout.verifyNoPendingTasks();
expect($scope.result).toBe("whatIexpect");
}));
// with your example
it('should pass', (done) => {
$timeout(50)
.then(() => {
// this is never called
expect(1).toBe(1);
})
.finally(done);
$timeout.flush();
});
Everything better explain here :)
In addition, you should never use the real $timeout because it will REALLY slowing down your tests...

Jasmine testing nested angular promises not returning promise

I am trying to write get the test working that has nested promises. For some reason the promise is never returning.
For example
describe('Some tests', function ()
{
it("Should complete the test with the nested promises", function(done)
{
inject(function (SomeService, $rootScope)
{
SomeService.somePromise().then(function(err)
{
console.log("Message 1");
expect(err).toBeNull();
SomeService.someOtherPromise.then(function(res)
{
console.log("Message 2");
// Check some data
done();
});
});
$rootScope.$digest();
});
});
});
The strange thing is, if this test is run by its self it passes. But if there is more than one of these tests it fails.
When the test fails it seems as though the first promise is never returned. So we don't even see the first console message.
My question is how to get this kind of test running and why is it not working correctly.

Angular / Karma - $http.get not executing

I have an AngularJS project which uses Karma to run some unit tests in the browser. I'm using mocha as the test framework.
However, I've got some specification tests which need to read some JSON files and check that they adhere to a given convention spec (types, name convention etc).
I should make it clear that it is the actual contents of these files that I want to test. Not a spoofed version of them through Angular Mock's $httpBackend.
I'm marking the JSON files for serving in karma.conf.js.
files: [
{ pattern: 'static/assets/json/cards/*.json', included: false, served: true },
'path/to/angular.js',
'path/to/angular-mocks.js',
'tests/**/*.js'
]
If I run karma start, I can browse over to /base/static/assets/json/cards/something.json and see that the files are being served.
Next, in my test, both the $http and the $q services are injected.
var $http, $q;
beforeEach(module('chai'));
beforeEach(inject(function(_$http_, _$q_) {
$http = _$http_;
$q = _$q_;
}));
Then I try to load each resource using $http.get. Finally, the promises returned from $http.get are collated and a call to $q.all is made in order to wait for them all to be done, before calling done() and moving on.
it('should load the resources', function(done) {
var promises = ['admissions.json', 'discharge.json']
.map(function(resource) {
console.log('Loading', resource);
return $http.get('/base/static/assets/json/cards/' + resource);
});
$q.all(promises)
.then(function(card) {
console.log('Success');
done();
}, function(err) {
console.log('Failure', err);
done();
});
});
When my tests run, I see following console output:
Loading admissions.json
Loading discharge.json
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
At first I assumed that it might have been exceeding the timeout by taking a long time to load, but the file is only 95kb.
Next, I wondered whether the custom promise interface (.success and .error) for $http was breaking the $q.all method. But apparently not.
Finally I tried to make a standalone request for /base/static/assets/json/cards/admissions.json at the beginning of all the tests.
It returns a promise, as expected, but it is never resolved, because no response is sent back. I checked the network tools to see what was coming back and it turns out that the request isn't even made in the first place. The code definitely runs, but for some reason $http doesn't actually make the request.
My inclination is that this is something to do with Angular Mocks intercepting $http requests for it's own $httpBackend service. How can I circumvent this?
I found a solution in this blog. The problem it's that you have to add the digest of the scope, even if you are not testing controllers.
it('does a thing one way', function() {
var value;
deferred.promise.then(function(_value_) {
value = _value_;
});
deferred.resolve(10);
expect(value).not. toBe(10); // not yet 10 because $digest hasn't run
$scope.$digest();
expect(value).toBe(10); // 10 because $digest already ran
});

How to mark a jasmine test as failed?

I have a jasmine 2.0 test that if a function is called, the test failed.
I have a function "Remote.get" that should call the first argument (which is a callback) if it is successful, or the second argument if it failed.
If it calls the second argument, I need to mark the test as failed.
How can I clearly mark the test as failed?
describe("my tests", function() {
it("should call the first function", function(done) {
Remote.get(
function() {
// yeah! good!
done();
},
function() {
// whoa, if we got here, then it didn't work
// fail()!
done();
}
);
});
});
I know I could do something like expect(true).toBe(false) but I the error you get then would be unclear and unrelated to the actual problem. It should give an error like "wrong callback was called" or "Remote.get failure was called". I was hoping there was something more descriptive in Jasmine.
What I'm really looking for is the python equivalent of http://docs.python.org/2/library/unittest.html#unittest.TestCase.fail.
They added a fail() method very recently in this commit. It will likely be released with the next point release of jasmine (assuming 2.1, unclear when this will be), or you can build your own from edge.
You could write and register a custom matcher whose compare function in the return value always fails, and assign your custom message to the message property

Categories