Why do I have to call spyOn in a beforeEach()? - javascript

I have a simple test suite that has one it function inside of it. I want to see if a certain function is called within the function I'm calling, so I have something like this:
describe("doStuff", function () {
var foo = new Foo();
spyOn(foo, "doOtherStuff");
foo.doStuff(true);
it("should do stuff and other stuff", function() {
expect(foo.stuffDone).toBe(true);
expect(foo.doOtherStuff).toHaveBeenCalled();
});
});
However, this gives me the error: Expected a spy, but got Function.
After looking around some, I saw all examples had the spyOn in a beforeEach. So, I changed my test to:
describe("doStuff", function () {
var foo = new Foo();
beforeEach(function() {
spyOn(foo, "doOtherStuff");
foo.doStuff(true);
});
it("should do stuff and other stuff", function() {
expect(foo.stuffDone).toBe(true);
expect(foo.doOtherStuff).toHaveBeenCalled();
});
});
And this works. I'm pretty new to jasmine, so I may just be missing something obvious, but I just want to know why it has to be in a beforeEach for the spyOn to work. It's easy enough to just use the beforeEach, but I want to understand better what is going on. Thanks.

That is simply because Jasmine runs the Specs in a different closure. The describe and it calls only register callbacks that are added to a queue and then executed by Jasmine later. And Jasmine always cleans up the spies ...
But you can also add the spyOn to the it callback.

Related

Understanding state in jest tests that use spyOn

Suppose that I have a test file foo.test.ts that looks like this:
describe("foo", function() {
let myMock;
beforeAll(function() {
myMock = jest.spyOn(someObject, "someMethod");
});
test("my test", function() {
beforeEach(function() {
myMock.mockReturnValue("someValue");
});
...
});
afterAll(function() {
myMock.mockRestore();
});
});
I'm using this pattern because I've seen it recommended in other StackOverflow answers. I wish to understand how Jest works though.
Do I really need to store the mock in a variable, or can I do this?
describe("foo", function() {
test("my test", function() {
beforeEach(function() {
jest.spyOn(someObject, "someMethod").mockReturnValueOnce("someValue");
});
...
});
});
Notice that I'm using mockReturnValueOnce instead of mockReturnValue.
I ran both implementations and they work fine, but I wonder whether I can say that they are equivalent? Will the "someMethod" function, part of "someObject", not be retained as a mock across other tests from other files?
jest.spyOn() returns a jest mock function and you want to store the returned mock function in a variable so that you can make assertions.
If you don't store the mock function returned by jest.mock(), then how will you make assertions? All the methods, provided by jest, that can be called on a mock function, can't be called without having a reference to the mock function.
If you just want to mock a return value and not make any assertions on the mocked function, then you don't need to save the reference. Generally, when you mock a function, you want to make assertions like how many times it was called, what arguments it was called with, etc.
Will the "someMethod" function, part of "someObject", not be retained
as a mock across other tests from other files?
someMethod will only be mocked in the test file where you mock it. It won't affect the other test files.

Testing Javascript function using Jasmine

I have a problem when I'm trying to test some javascript, I'm not sure that the code is actually testable, but then I would be pleased to know how to make it testable.
$("#Button").on('touchstart', () => testFunc());
function testFunc() {
button(true, userInfo, gameCode);
}
I want to test when the button is touched that the testFunc is called. I'm using Jasmine as my test framework.
But for whatever reason when I try to call the testFunc from Jasmine just to test that something is working I get the error
"ReferenceError: Can't find variable: testClass in file:///E:/Dokumenter/SemesterProjekt%20fun%20stuff/PRJ4Web/TankWebApplication/TankWebApplication/Test/Test.js (line 8)s"
I have made the reference to the file. So I'm not sure what is going on.
The test code that gives me the error
describe("Joystick test for functionallity",
function() {
beforeEach(function() {
});
it("All default values",
function() {
testFunc();
});
});
How do I test this code? Is it possible to do?
Your goal is to see if the function will be called upon button click, therefore, you must check the actual function call with
toHaveBeenCalled jasmine method.
Invoking the functions by itself doesn't make any sense (like in your example)
You should do something like this:
it('All default values', => {
spyOn(class.testFunc);
document.getElementById('Button').click();
expect(class.testFunc).toHaveBeenCalled();
}));
})

Do you need spies to test if a function has been called in Jasmine?

Am learning Jasmine, wondering if the following test would be valid? And if not, can someone explain why? I've been reading a number of tutorials and can't find a good explanation that has helped me understand why I can't seem to write a test like the one below correctly.
// spec
describe("when cart is clicked", function() {
it("should call the populateNotes function", function() {
$("#show-cart").click()
expect(populateNotes()).toHaveBeenCalled();
})
})
// code
$("#show-cart").click(function() {
populateNotes();
})
You need to do two things, first you need to spy on the function before the click. Normally you would spy on a function like this that is a member of an object. Where is populateNotes defined? You need a reference to it somehow.
// This might work, if the function is defined globally.
spyOn(window, 'populateNotes');
// Then do your action that should result in that func being called
$("#show-cart").click();
// Then your expectation. The expectation should be on the function
// itself, not on the result. So no parens.
expect(window.populateNotes).toHaveBeenCalled();

Asserting that a callback has been passed with the correct scope

I'm trying to find a way to test that ObjectA is correctly setting up a callback from ObjectB to ObjectC.
e.g.
// In class (has scope issue)
this._processor.process('stuff', this._handler.handle);
// In test
expect(processor.process).toHaveBeenCalledWith('stuff', handler.handle);
What I went to test is that the following call actually takes place:
this._processor.process('stuff', this._handler.handle.bind(this._handler));
I'm aware I can fix this by handling the callback like so:
this._processor.process('stuff', function() {
this._handler.handle()
});
And testing that the 'handler' spy gets called on callback from the process function (this is how I'm generally doing things at the moment). But setting it all up makes the test messy and adds code and complexity to the class under test purely to make it testable.
In the general sense, each call to a spy tracks the scope in the object property (assuming Jasmine 1.3):
it("has the right scope", function () {
var scopeObj = {foo: "bar"};
var spy = jasmine.createSpy("scope");
spy.call(scopeObj, 42, "blue");
// Also can use spy.calls[0].object
expect(spy.mostRecentCall.object).toBe(scopeObj);
});

Unit testing with Jasmine: code in beforeEach() not seen by test's spyOn()

New to unit testing in general and Jasmine in particular.
I've set a variable in a beforeEach() callback, but it doesn't seem to work on the second test. It's supposed to fire initialization stuff in advance of every test within its context, right? I'm sure my spyOn() call is to blame, but I don't know how to fix it.
Comments explain the passes and fails:
describe("Test suite for my library", function () {
var html,
body,
play,
...
// custom matcher...
beforeEach(function () {
this.addMatchers({
toBeInstanceOf : function (constructr) {
return this.actual instanceof constructr;
});
});
});
describe("Within the Button object", function () {
beforeEach(function () {
play = new Button("play", false);
});
describe("play", function () {
// This test passes, as expected...
it("should be an instance of the Button object", function () {
expect(play).toBeInstanceOf(Button);
});
});
describe("play.name", function () {
// This test failed with the message
// "Expected spy Button to have been called
// with [ 'play', false ] but it was never called."
it("should be the first argument passed to the Button constructor", function () {
spyOn(window, "Button");
play = new Button("play", false); // ...until I added this line. Now it passes.
expect(window.Button).toHaveBeenCalledWith("play", false);
});
// This test passes, even if the one above fails.
it("should be 'play'", function () {
expect(play.name).toBe("play");
});
});
});
});
The documentation explains the usage, but not the context, of spyOn(), so I can't tell if I've created a bug or if I'm unknowingly taking advantage of a feature.
I can post the constructor if anyone thinks it makes any difference in the diagnosis, but I can assure you it's dead simple.
I'm sure it's a straightforward fix using some basic unit testing concept I'm having to learn the hard way. Thanks in advance.
P.S. I realize what I'm testing for in that failing spec isn't what I've described. I'm working my way through the API guide, looking for a way to get to the arguments array within a function call, so I can do a specific test on arguments[0]. Hints are appreciated, but not necessary. I'll figure it out.
Short answer: No, Before each and spies are not incompatible
You must Spy before you call if you want the spy to know about the call. You can use spyOn(object, 'function').andCallThrough() if you do not wish to interfere with its default behavior.
Long answer: The way faking/mocking/stubbing/spying frameworks often work is by replacing the method you are calling with a method that the mocking framework can control. Any calls to that function before it is replaced with the spy cannot be observed. This is a good thing, though mildly inconvenient,
Its cause you spy on window.Button after you have called. Im not totally sure what spy does, but after all it displaced the function you spy on with another function where it can check the function was called and whats arguments was passed. When you create your Button before your start your test, the original window.button function called. Then you replaces the function with the spy and test that the spy was called, so your test must fail.
Seems either create your Button in the test itself or create your spy before you call new Button in your beforeEach function.

Categories