How to test the expectation on the eventSpy - javascript

I am trying to test a backbone.model when saving.
Here's my piece of code.
As you can see from the comment there is a problem with toHaveBeenCalledOnce method.
P.S.:
I am using jasmine 1.2.0 and Sinon.JS 1.3.4
describe('when saving', function ()
{
beforeEach(function () {
this.server = sinon.fakeServer.create();
this.responseBody = '{"id":3,"title":"Hello","tags":["garden","weekend"]}';
this.server.respondWith(
'POST',
Routing.generate(this.apiName),
[
200, {'Content-Type': 'application/json'}, this.responseBody
]
);
this.eventSpy = sinon.spy();
});
afterEach(function() {
this.server.restore();
});
it('should not save when title is empty', function() {
this.model.bind('error', this.eventSpy);
this.model.save({'title': ''});
expect(this.eventSpy).toHaveBeenCalledOnce(); // TypeError: Object [object Object] has no method 'toHaveBeenCalledOnce'
expect(this.eventSpy).toHaveBeenCalledWith(this.model, 'cannot have an empty title');
});
});
console.log(expect(this.eventSpy));

Jasmine has no function toHaveBeenCalledOnce. You need to check the count yourself.
expect(this.eventSpy).toHaveBeenCalled();
expect(this.eventSpy.callCount).toBe(1);
So I guess in your case, you'd want this:
expect(this.eventSpy.callCount).toBe(1);
expect(this.eventSpy).toHaveBeenCalledWith(this.model, 'cannot have an empty title');
Updated
The error you are getting now, "Expected a spy, but got Function" is because of exactly that. You are using a Sinon library Spy, and passing it to a Jasmine function that expects a Jasmine Spy.
You should do either:
this.eventSpy = jasmine.createSpy();
or
expect(this.eventSpy.calledOnce).toBe(true);
expect(this.eventSpt.calledWith(this.model, 'cannot have an empty title')).toBe(true);
What was your reasoning behind using Sinon along with Jasmine? I'd recommend the first solution, since then Jasmine will have more info to show when the test fails.

There's a library called jasmine-sinon that adds sinon-specific matchers to jasmine.
It allows you to do things like
expect(mySpy).toHaveBeenCalledOnce();
expect(mySpy).toHaveBeenCalledBefore(myOtherSpy);
expect(mySpy).toHaveBeenCalledWith('arg1', 'arg2', 'arg3');

Try with the toHaveBeenCalledTimes method:
expect(this.eventSpy).toHaveBeenCalledTimes(1);

Related

Protractor is not able to find a function from JS file

I created a function which return the new Browser object from the JS function browser.forkNewDriverInstance() and i created a global variable in my config file and i'm calling a function from that file by using this global variable. but here when i'm calling that function it is throwing error like utility.openNewBrowser is not a function error.
Config File:
onPrepare: function () {
global.utility=require("../src/test/resources/com.learnFramework.utility/timeOutConfig.js");
}
Cucumber Opts functions
cucumberOpts: {
//i'm using the same file for setting up the timeout.. is this creating the issue??
require:['../src/test/resources/com.learnFramework.utility/timeOutConfig.js'],
tags: false,
profile: false,
format:'json:../Reports/jsonResult/results.json',
'no-source': true
}
Function
var configure = function () {
this.setDefaultTimeout(100 * 1000);
this.openNewBrowser=function(){
return browser.forkNewDriverInstance(true);
}
};
module.exports = configure;
Error Log
TypeError: utility.openNewBrowser is not a function
When i called the forkNewBrowserInstance method directly i'm getting the below error.
both angularJS testability and angular testability are undefined. This could be either because this is a non-angular page or because your test involves client-side navigation, which can interfere with Protractor's bootstrapping. See http://git.io/v4gXM for details
can some help me to resolve this issue.. i got this error because the first browser ignoring synchronization but second browser how can i ignore the synchronization?
What you did above is the correct way of defining a global variable. But I think the global variable name should be configure instead of utility that is why it is throwing the TypeError.
And wherever you want to call it, use that variable name as is. This is actually how protractor, browser and other built-in global variables were made available globally. The following posting was helpful and also the protractor doc where it's explaining the property: params?: any;
Hope this helps, please let me know.
I faced the same issue but with jasmine.
so i had done the following workaround and issue was resolved.
class abc{
constructor() {
var configure = function () {
this.setDefaultTimeout(100 * 1000);
this.openNewBrowser=function(){
return browser.forkNewDriverInstance(true);
}
};
}
}
module.exports = new abc();

getText is not a function error - Protractor (javascript)

I have node.js installed and protractor installed. I have experience with selenium-webdriver but Protractor is driving me nuts!!! I am also not that familiar with javascript.
This is what my code looks like:
describe('My app', function() {
var result = element(by.id('result-name'));
var enterBtn = element(by.id('enter'));
var clearFieldBtn = element(by.id('clear-field');
it('should bring up components on load', function() {
browser.get(`http://localhost:${process.env.PORT}`);
browser.wait(until.titleContains('Sample App'), 500);
browser.wait(until.presenceOf(browser.element(by.id('my-test-app'))), 500);
expect(enterBtn).isPresent;
});
it('result should equal username', function () {
browser.get(`http://localhost:${process.env.PORT}`);
expect(clearFieldBtn).isPresent;
expect(result.getText()).toEqual('John Smith'); //both tests pass without this line of code
});
});
The last line "expect(result.getText()).toEqual('John Smith');" throws me an error. I get:
expect(...).toEqual is not a function
Any help would be much appreciated. I have spent a couple of hours trying to find a solution and trying different things.
I also wanted to implement the isPresent function how it's done in the api docs which is like this: expect($('.item').isPresent()).toBeTruthy();
I tried to do:
expect(clearFieldBtn).isPresent().toBeTruthy();
But I get that isPresent is not a function...
The expect above that line seems poor. It should read
expect(clearFieldBtn.isPresent()).toBeTruthy();
not sure if that is causing the weird error on the line below...just thought I would throw it out there. All your protractor APIs need be be called within the expect because isPresent is not a attribute of expect
Have you tried these lines:
clearFieldBtn.isPresent().then(function(bln) {
expect(bln).toBe(true);
});
result.getText().then(function(tmpText) {
expect(tmpText).toBe('John Smith');
});
If you still get an error on result.getText(), please check the presence of the result object.

How to use chakramjs for a typescript project?

I am writing a node application in typescript, and am pretty inexperienced with both nodejs and typescript.
I want to use chakram to test the API endpoints, yet chakram lacks typescript definiton.
The more general question is how to import a library without definitions, yet the way I am supposed to to apply the answers still eludes me. The provided answer are too abstract for my current understanding, so I would like a more concrete example.
Basically, I don't know how to transform the working javascript healthcheck.js:
var chakram = require('chakram'),
expect = chakram.expect;
describe("Rest API Healthceck", function () {
it('should respond with HTTP STATUS OK NO CONTENT', function () {
var response = chakram.get("http://app.local/api/status", {});
expect(response).to.have.status(204);
return chakram.wait();
});
});
into its typescript variant.
I tried to work with any, as I don't want to provide my own typings yet, I just want it to work.
I tried im my healthcheck.ts file with:
let it: any;
let describe: any;
let chakram: any;
chakram = require('chakram');
const expect = chakram.expect;
describe("Rest API Healthceck", function () {
it('should respond with HTTP STATUS OK NO CONTENT', function () {
var response = chakram.get("http://app.local/api/status", {});
expect(response).to.have.status(204);
return chakram.wait();
});
});
It does compile yet it throws an error if I try to execute the test with mocha by ./node_modules/mocha/bin/mocha dist/tests/acceptance/healthcheck.js, namely:
TypeError: describe is not a function
Investigating the error further I am also not sure if the issue has to do with mocha and how its types are loaded. I am also using typings and not definilty typed, that may also be another problem.
Where am I going wrong?
To make the test run I had to make typings aware of mocha:
./node_modules/typings/dist/bin.js install env~mocha --global
The problem was due to missing defintion of mocha and unrelated to chakramjs.
In order for it to run then, my typescript testcase looks like:
let chakram: any;
chakram = require('chakram');
const expect = chakram.expect;
describe("Rest API Healthceck", function () {
it('should respond with HTTP STATUS OK NO CONTENT', function () {
var response = chakram.get("http://app.local/api/status", {});
expect(response).to.have.status(204);
return chakram.wait();
});
});

Stubbing window.location.href with Sinon

I am trying to test some client-side code and for that I need to stub the value of window.location.href property using Mocha/Sinon.
What I have tried so far (using this example):
describe('Logger', () => {
it('should compose a Log', () => {
var stub = sinon.stub(window.location, 'href', 'http://www.foo.com');
});
});
The runner displays the following error:
TypeError: Custom stub should be a function or a property descriptor
Changing the test code to:
describe('Logger', () => {
it('should compose a Log', () => {
var stub = sinon.stub(window.location, 'href', {
value: 'foo'
});
});
});
Which yields this error:
TypeError: Attempted to wrap string property href as function
Passing a function as third argument to sinon.stub doesn't work either.
Is there a way to provide a fake window.location.href string, also avoiding redirection (since I'm testing in the browser)?
You need to use global to mock the window object for your test in beforeEach or it
e.g.
it('should compose a Log', () => {
global.window = {
location: {
href: {
value: 'foo'
}
}
}
//.... call the funciton
});
Stubs cannot replace attributes, only functions.
The error thrown reinforces this:
TypeError: Custom stub should be a function or a property descriptor
From the documentation:
When to use stubs?
Use a stub when you want to:
Control a method’s behavior from a test to force the code down a specific path. Examples include forcing a method to throw an error in order to test error handling.
When you want to prevent a specific method from being called directly (possibly because it triggers undesired behavior, such as a XMLHttpRequest or similar).
http://sinonjs.org/releases/v2.0.0/stubs/
Possible solution
While many builtin objects can be replaced (for testing) some can't. For those attributes you could create facade objects which you then have to use in your code and being able to replace them in tests.
For example:
var loc = {
setLocationHref: function(newHref) {
window.location.href = newHref;
},
getLocationHref: function() {
return window.location.href;
}
};
Usage:
loc.setLocationHref('http://acme.com');
You can then in your test write
var stub = sinon.stub(loc, 'setLocationHref').returns('http://www.foo.com');
Note the chained returns() call. There was another error in your code: the third argument has to be a function, not value on another type. It's a callback, not what the attribute should return.
See the source code of stub()
Use window.location.assign(url) instead of overwriting the value of window.location. Then you can just stub the assign method on the window.location object.
http://www.w3schools.com/jsref/met_loc_assign.asp
UPDATE: I tested this in a headless browser, but it may not work if you run your tests in Chrome. See #try-catch-finally's response.

Front end javascript testing using Require and Resharper

So I've been trying to figure out how front end testing works (unit testing) but I am getting stuck on some point.
So I have my jasmine test set up as follows:
describe('Blabla', function () {
it('returns true', function () {
var people = require(["people"], function(ppl) {
return ppl;
});
expect(people.getTitle()).toBe('People piolmjage');
});
});
But running this gets me:
TypeError: undefined is not a funtion
So obviously, people is undefined. So perhaps my callback comes in too late. But if I remove the callback I get following error:
it('returns true', function () {
var people = require("people");
expect(people.getTitle()).toBe('People piolmjage');
});
Error: Module name "people" has not been loaded yet for context: _. Use require([])
I figure there is something wrong in my setup...Anyone have any idea how to get this FE testing to work?
I did manage to get it to work from console and using define combined with phantomjs and the durandal test files but I need this to work outside of the console and hereby I cannot use this define because the test runner won't find my tests.
That's why I need to use the CommonJS was of getting the required viewmodels.
people model
define([],
function () {
var getTitle = function() {
return "hello";
}
var peopleViewModel = {
title: 'People page',
getTitle: getTitle
};
return peopleViewModel;
});
UPDATE
I got the code working but not with resharper. Following this page from the durandal webpage.
But this gets me console output which is way to unstructured to actually read through.
I can however use the define keyword and then it works fine. So I assume it is the require keyword where I mess up something?
UPDATE 2
So I used fiddler to check what is going on. I also finally got it working (kinda...).
My testfile looks like this now:
///<reference path="../../Scripts/require.js"/>
///<reference path="../../test/lib/jasmine-2.1.3/jasmine.js"/>
///<reference path="../../App/viewmodels/people.js"/>
describe('Blabla', function () {
it('require test', function (done) {
require(['people'], function (people) {
expect(people.title).toBe('People page');
done();
});
});
});
And then I changed my people file:
define("people", ["bla"], function (bla) {
return {
title: 'People page',
bla: bla
};
});
As you can see here, I name my viewmodel to be people.
This works for the testrunner but he doesn't actually get any files through requireJS but only the reference paths. Also this does not fit my needs because the durandal models are unnamed.
Fiddler screenshot:
So basically he does not use requireJS to get the viewmodels and therefor I cannot just use the require.config initializer to get to my viewmodels folder and download every viewmodel using requireJS. Any thoughts?
I finally got it working, took me like a day and a half.
Anyway I don't use resharper anymore, or it's test runner to be more precise.
Chutzpah is the one I turned to in the end. This too took me some research but I got it to the point where it includes everything as I want it to.
Check this post for sure
Here is what I did:
My people.js looks like this:
define(['viewmodels/bla'], function (bla) {
return {
title: 'People page',
bla: bla //testing dependencies on other viewmodels
};
});
Then I also made a bla.js
define(function() {
return {
bla: "bla"
};
});
And now for the tests:
describe('Blabla', function () {
it('require test', function (done) {
require(['viewmodels/people'], function (people) {
expect(people.title).toBe('People page');
done();
});
});
it('dependency on require test', function (done) {
require(['viewmodels/people'], function (people) {
console.log(people.bla);
expect(people.bla.bla).toBe('bla');
done();
});
});
});
And then eventually, reading the answers on the link provided on top I had to create a Chutzpah config file to create a test harnass:
{
"Framework": "jasmine",
"TestHarnessReferenceMode": "AMD",
"TestHarnessLocationMode": "SettingsFileAdjacent",
"References" : [
{"Path" : "../Scripts/require.js" },
{"Path" : "requireConfig.js" }
],
"Tests" : [
{"Path": "specs"}
]
}
Now, running the tests with Visual studio test runner actually gets me everything I need and as you can see, I can now access all my viewmodels through require like so: require(['viewmodels/whateverviewmodel'], function(whateverviewmodel){....})
I hope this answer can get people on their way to testing your (Durandal)SPA using Jasmine and RequireJS.
I know my viewmodels in this answer, nor in the question itself, say much but this should get you an idea of how to go about all of this.
Small Edit
You can now also skip the callback mess with require([]... inside of the tests and build your tests like you do your viewmodels with define
define(['viewmodels/people'], function (people) {
describe('Blabla', function () {
it('require test', function () {
expect(people.title).toBe('People page');
});
it('dependency on require test', function () {
console.log(people.bla);
expect(people.bla.bla).toBe('bla');
});
});
});
This gets you less indents and is more readable in itself.
The require call provided by RequireJS is inherently asynchronous so you need to do something like this:
it('returns true', function (done) {
require(["people"], function(people) {
expect(people.getTitle()).toBe('People piolmjage');
done(); // Signal that the test is done.
});
});
The first attempt you show in your question cannot work. That's the classical "trying to return values synchronously form asynchronous code" mistake. The second attempt with require("people") does not work either because this require call is pseudo-synchronous and will work only if the module requested is already loaded. See this answer for an explanation of how this pseudo-synchronous require works.

Categories