To what extent can we refactor unit tests - javascript

When working with Jasmine testing framework, I have come across a code snippet where expect is written in a sub-function which is called in it(), but not in it() itself. The reason behind writing this is, they were trying to compare similar objects and they refactored test code and moved this expect to the sub-function. Now, we don't have expect in it() rather we have a method call which has expect.
describe("bla bla", function() { //for nunit, junit guys, this is testSuite()
function somefunc() { //this is [test]
//do some stuff and generate object
expect(someObject).toEqual(otherObject); //expect is assert
}
it("some case", function() {
//do some stuff
somefunc();
});
it("some other case", function() {
//do some other stuff
somefunc();
});
});
Now, is this kind of test code encouragable? can we have it() without expect ?

I would argue that the two examples below are both readable. If assertExpected() did some kind of setup for otherObject the first example could be more readable.
describe("bla bla", function() { //for nunit, junit guys, this is testSuite()
function assertExpected() { //this is [test]
//do some stuff and generate object
expect(someObject).toEqual(otherObject); //expect is assert
}
it("some case", function() {
// Arrange
var someObject;
// Act
someObject = testMethod('foo');
// Assert
assertExpected();
});
it("some other case", function() {
// Arrange
var otherObject, someObject = testMethod('foo');
// Act
someObject = testMethod('foo');
// Assert
assert(someObject(foo)).toEqual(otherObject);
});
});
I think the deciding factor here should be deciding on a house style and sticking with it. There's no clear winner here and this question probably isn't a good question for SO.

Related

javascript bind callback to any function

I would like to bind or chain a callback/function to another function.
There is a similar question (without any valid answer): jQuery , bind a callback to any function
But, for me I would not want to be limited only to jQuery realm.
I looking for a broader answer, I wonder if it's possible with vanilla javascript or library other than jQuery.
Example:
// a function which I don't want or I can't edit it's body
var thirdPartyObject = {
doSomething: function(args) {
// a lot of code
console.log('doing stuff: ' + args);
}
};
// my function
var easyCallback = function() {
// processing stuff
console.log('doing more stuff');
}
// the bind
magicLibrary.bind(thirdPartyObject.doSomething, easyCallback);
// run
thirdPartyObject.doSomething(1);
thirdPartyObject.doSomething(10);
When I run this "code", the following output represents the behaviour I'm looking for:
doing stuff: 1
doing more stuff
doing stuff: 10
doing more stuff
Is it possible?
EDIT: the bind is a conceptual term, maybe you think this like a chain, trigger or even another term.
But the import is the second function or callback which in my example is easyCallback() must be somehow connected to the first one doSomething().
And every time the doSomething() is called or executed I want the easyCallback() to be executed as well after the first is finished.
But the without "wrapping" them around or without rewriting the first one.
You would have to wrap the doSomething function inside yet another function, like so:
// a function which I don't want or I can't edit it's body
var thirdPartyObject = {
doSomething: function(args) {
// a lot of code
console.log('doing stuff: ' + args);
}
};
// my function
var easyCallback = function() {
// processing stuff
console.log('doing more stuff');
}
// the bind
// magicLibrary.bind(thirdPartyObject.doSomething, easyCallback);
const doSomething = thirdPartyObject.doSomething.bind(thirdPartyObject);
thirdPartyObject.doSomething = function(args) {
doSomething(args);
easyCallback(args);
};
// run
thirdPartyObject.doSomething(1);
thirdPartyObject.doSomething(10);

Jasmine testing methods inside .done or .then

Wondering if anyone can help me - I'm trying to test my js using Jasmine (1.3) and I can't figure out the best way to test any method calls inside a .then or a .done method.
example code to explain:
Backbone.View.extend({
myMethod: function () {
this.something.done(function () {
this.doSomethingElse();
}.bind(this));
}
})
I'd like to write a test that check that this.doSomethingElse was called.
I was looking around at jasmine.async and a waitsFor/runs set up but I'm not sure how it fits into external code i.e. I'm not going to call done() inside my actual code to get my test working. Also if I mock out the done method on this.something then I'm not longer testing the actual implementation, right?
I'm just missing how things fit together. If anyone could point me in the right direction I'd really appreciate it!
Update: based on feedback below I've now tried the following
Hey, thanks for the answer - I think maybe I don't have the last part correct - have tried 2 different ways, both initial pass but then fail after a second or 2.
it('calls doSomethingElse on done',function () {
var mockDeferred = $.Deferred();
myView.something = mockDeferred;
spyOn(myView,'doSomethingElse');
mockDeferred.resolve();
waitsFor(function () {
expect(myView.doSomethingElse).toHaveBeenCalled();
});
});
And also:
it('calls doSomethingElse on done',function () {
var mockDeferred = $.Deferred(),
someTrigger = false;
myView.something = mockDeferred;
spyOn(myView,'doSomethingElse');
runs(function () {
mockDeferred.resolve();
someTrigger = true;
});
waitsFor(function () {
someTrigger = true;
});
runs(function () {
expect(myView.doSomethingElse).toHaveBeenCalled();
});
});
In both instances the test will pass originally but then timeout to a failure after a second or 2.
Am I missing something?
To test the example function you described, I would do the following within your test:
Create a new deferred object (I'll call it mockDeferred)
Pass mockDeferred into your code under test so that it is now this.something in your example
Spy on the doSomethingElse function
Call myMethod()
Call resolve() on mockDeferred
Assert that doSomethingElse was called
Edit based on OP's Update:
I don't see anywhere in either of your examples where you are calling myView.myMethod() within your test; make sure you do that. I whipped up an example that you can reference here.
As an aside, I'm surprised the second example you tried passes initially. Maybe because you have some code outside of a runs() block?
Related problem
Spying on a method inside .then and expecting .toHaveBeenCalled fails
Solution:
run test inside fakeAsync and run tick() before the expect
Service:
getFirebaseDoc() {
this.db.firestore.doc('some-doc').get()
.then(this.getFirebaseDocThen)
.catch(this.getFirebaseDocCatch);
}
Unit testing:
it('should call getFirebaseDocThen', fakeAsync(() => { // note `fakeAsync`
spyOn(service, 'getFirebaseDocThen');
spyOn(service.db.firestore, 'doc').and.returnValue({
get: (): any => {
return new Promise((resolve: any, reject: any): any => {
return resolve({ exists: true });
});
},
});
service.getFirebaseDoc();
tick(); // note `tick()`
expect(service.getFirebaseDocThen).toHaveBeenCalled();
}));

Error in testing a Meteor helper function with Jasmine

I have a helper function in MeteorJS as given below:
Template.cars.helpers({
models : function() {
var currentUserId = Meteor.userId();
return cars.find({userId: currentUserId});
}
});
I am trying to write a Jasmine test which will check if the Meteor.userId() function is called once when the models helper is called. I have mocked the Meteor.userId() function,and my code is given below:
describe("test cars collection", function() {
beforeEach(function() {
var Meteor = {
userId: function() {
return 1;
}
};
});
it("userId should be called once", function() {
Template.cars.__helpers[' models']();
spyOn(Meteor, 'userId').and.callThrough();
expect(Meteor.userId.calls.count()).toBe(1);
});
});
However, the result is showing Expected 0 to be 1. I am new to Jasmine and do not really know how to make the correct call to the Meteor.userId() function, while calling the models helper. I think the way I am spying on it is wrong, but I have not been able to figure it out. Can somebody help please?
From the Jasmine Docs
.calls.count(): returns the number of times the spy was called
But you need to set the spy before you call the function, so you can check if your function has been called only once like so:
it("userId should be called once", function() {
spyOn(Meteor, 'userId').and.callThrough(); // <-- spy on the function call first
Template.cars.__helpers[' models'](); // <-- execute the code which calls `Meteor.userId()`
expect(Meteor.userId.calls.count()).toBe(1); // <-- Now this should work
});

How do I wrap Mocha/Chai tests within asynchronous functions?

I am working on a framework with modules which are loaded asynchronously using promises. These modules contain methods (which, for this question, can be assumed to be synchronous) for which I would like to create tests for.
Currently, my code resembles the following:
describe("StringHelper", function() {
describe("convertToCamelCase()", function() {
it("should convert snake-cased strings to camel-case", function(done) {
Am.Module.load("Util").then(function() {
var StringHelper = Am.Module.get("Util").StringHelper;
//Test here
done();
});
});
});
describe("convertToSnakeCase()", function() {
it("should convert camel-cased strings to snake case.", function(done) {
Am.Module.load("Util").then(function() {
var StringHelper = Am.Module.get("Util").StringHelper;
//Another test here
done();
});
});
});
});
Given that Am.Module.load() is essentially a call to RequireJS wrapped in such a way to return a promise, and hence, should be only loaded once at the beginning, how can I rewrite the above?
I'm essentially hoping to have something like this:
Am.Module.load("Util").then(function() {
var StringHelper = Am.Module.get("Util").StringHelper;
describe("StringHelper", function() {
describe("convertToCamelCase()", function() {
it("should convert snake-cased strings to camel-case", function(done) {
//Test here
done();
});
});
describe("convertToSnakeCase()", function() {
it("should convert camel-cased strings to snake case.", function(done) {
//Another test here
done();
});
});
});
});
Unfortunately, the above does not work - the tests are simply not being executed. The reporter doesn't even display the part for describe("StringHelper"). Interestingly, after playing around, this only occurs if all the tests are written in this (the second code snippet) manner. As long as there is at least one test written in the first format, the tests show up properly.
You can use Mocha's before() hook to load the Util module asynchronously.
describe("StringHelper", function() {
var StringHandler;
before(function(done) {
Am.Module.load("Util").then(function() {
StringHelper = Am.Module.get("Util").StringHelper;
done();
});
});
describe("convertToCamelCase()", function() {
it("should convert snake-cased strings to camel-case", function() {
//Test here
});
});
describe("convertToSnakeCase()", function() {
it("should convert camel-cased strings to snake case.", function() {
//Another test here
});
});
});

Only the first test is actually run in Jasmine

I have the following code:
describe("Player", function() {
var player;
beforeEach(function(){
player = new Player({
name: "Mark"
});
});
it("has a name", function() {
expect(player.name).toEqual("Mark");
});
it("has a score"), function() {
expect(player.score).toEqual(0);
};
});
Jasmine says it's passing 2 specs, even though player.score is undefined
If I do...
it("has a score"), function() {
console.log("hello")
expect(player.score).toEqual(0);
};
I can see the second test is never run. Any ideas why? (this is my first time using Jasmine).
There is a misplaced closing parenthesis in the second spec (it() call). It should read:
it("has a score", function() {
expect(player.score).toEqual(0);
});
I run into this issue a lot: syntax errors in Jasmine specs result in tests not even running, rather than failing as they should. (I believe there may be an outstanding bug against Jasmine for this.)

Categories

Resources