I recently added an e.preventDefault() to one of my javascript functions and it broke my jasmine spec. I've tried spyOn(e, 'preventDefault').andReturn(true); but I get e is undefined error. How do I stub e.preventDefault()?
showTopic: function(e) {
e.preventDefault();
midParent.prototype.showTopic.call(this, this.model, popup);
this.topic.render();
}
it("calls the parent", function() {
var parentSpy = spyOn(midParent.prototype, "showTopic");
this.view.topic = {
render: function() {}
};
this.view.showTopic();
expect(parentSpy).toHaveBeenCalled();
});
Another way to create mock object (with spies you need) is to use jasmine.createSpyObj().
Array containing spy names have to be passed as second parameter.
var e = jasmine.createSpyObj('e', [ 'preventDefault' ]);
this.view.showTopic(e);
expect(e.preventDefault).toHaveBeenCalled();
You have to pass an object with a field preventDefault that holds your spy:
var event = {preventDefault: jasmine.createSpy()}
this.view.showTopic(event);
expect(event.preventDefault).toHaveBeenCalled
don't know this approach is correct or not but works for me, feel free to tell if it is not a good approach
step1:create a eventStub with the function for prevent default
const eventStub = {
preventDeafult() {}
}
step2: write the 'it' Block:
it('should preventDefault when dragover event occurs', () => {
// step 3 goes here
// step 4 goes here
// step 5 goes here
});
step3:create spy for prevent default:
const spy = spyOn(eventStub, 'preventDefault');
step4:trigger the event let's say dragover and pass the eventStub
component.triggerEventHandler('dragover', eventStub)
step5:write the assertion
expect(spy).tohaveBeenCalled()
Note: "component" in step 4 is the instance of component we get from fixture
Example to get component instance from fixture:
let fixture = TestBed.createComponent(<your Component's Class Name>);
let component = fixture.componentInstance;
try and let me know if it works for you thanks you,,,, Happyyy coding :-) !!!!!
This is very similar to approaches on top. I just mocked out the event and passed preventDefault with an sinon spy. The difference was that I had to identify the type which was a click on my test.
var e = {
type: 'click',
preventDefault: sinon.spy()
};
Related
I already visited this link and tried to follow some examples: Perform debounce in React.js
A bit of context: I'm building a search box that I want to deploy on NPM. Each time the user types, a prop function onSearch is called. This to allow the programmers to fetch new data if they want.
The problem: each character typed will trigger onSearch, but that's not optimal, so I want to debounce that.
I wanted to do as one of the posts suggests:
import React, { useCallback } from "react";
import { debounce } from "lodash";
const handler = useCallback(debounce(someFunction, 2000), []);
const onChange = (event) => {
// perform any event related action here
handler();
};
My problem is that I need to pass an argument to "someFunction", and that argument is a state (a string):
const [searchString, setSearchString] = React.useState("");
After various attempts I finally found a solution. Remembering how I debounced the window resize event in the past, I followed more or less the same pattern. I did it by attaching an event listener to the window object and by adding a property to the event when dispatching it. It works, but is it a good solution? Is there a better way to achieve this?
React.useEffect( ()=> {
// This will contain the keyword searched when the event is dispatched (the value is stored in event.keyword)
// the only function dispatching the event is handleSetSearchString
// It's declared at this level so that it can be accessed from debounceDispatchToParent
let keyword = "";
// This function contains the onSearch function that will be debounced, inputDebounce is 200ms
const debounceDispatchToParent = debounce(() =>
onSearch(keyword, isCached("search-keyword-" + keyword)), inputDebounce);
// This function sets the keyword and calls debounceDispatchToParent
const eventListenerFunction = (e) => {
// the event has a property attached that contains the keyword
// store that value in keyword
keyword = e.keyword;
// call the function that will debounce onSearch
debounceDispatchToParent();
}
// Add the listener to the window object
window.addEventListener("dispatchToParent", eventListenerFunction, false);
// Clean up
return ()=> window.removeEventListener("dispacthToParent", eventListenerFunction);
}, []);
Then everytime the user types I call handleSetSearchString:
const handleSetSearchString = keyword => {
keyword = keyword.toLowerCase();
// If the string is longer than the minimum characters required to trigger a filter/search
if (keyword.length > minChars) {
// Here I create the event that contains the keyword
const event = new Event("dispatchToParent");
event.keyword = keyword;
window.dispatchEvent(event);
} else if (keyword.length === 0) {
// If the string is empty clear the results
setFilteredItems([]);
}
setSearchString(keyword);
};
Since both debounce and useCallback return a function you could just pass it directly.
const handler = useCallback(debounce(someFunction, 2000), []);
const onChange = (event) => {
// perform any event related action here
handler(argument1, argument2, ...args);
};
I'm trying to mock an ES6 class with a constructor that receives parameters, and then mock different class functions on the class to continue with testing, using Jest.
Problem is I can't find any documents on how to approach this problem. I've already seen this post, but it doesn't resolve my problem, because the OP in fact didn't even need to mock the class! The other answer in that post also doesn't elaborate at all, doesn't point to any documentation online and will not lead to reproduceable knowledge, since it's just a block of code.
So say I have the following class:
//socket.js;
module.exports = class Socket extends EventEmitter {
constructor(id, password) {
super();
this.id = id;
this.password = password;
this.state = constants.socket.INITIALIZING;
}
connect() {
// Well this connects and so on...
}
};
//__tests__/socket.js
jest.mock('./../socket');
const Socket = require('./../socket');
const socket = new Socket(1, 'password');
expect(Socket).toHaveBeenCalledTimes(1);
socket.connect()
expect(Socket.mock.calls[0][1]).toBe(1);
expect(Socket.mock.calls[0][2]).toBe('password');
As obvious, the way I'm trying to mock Socket and the class function connect on it is wrong, but I can't find the right way to do so.
Please explain, in your answer, the logical steps you make to mock this and why each of them is necessary + provide external links to Jest official docs if possible!
Thanks for the help!
Update:
All this info and more has now been added to the Jest docs in a new guide, "ES6 Class Mocks."
Full disclosure: I wrote it. :-)
The key to mocking ES6 classes is knowing that an ES6 class is a function. Therefore, the mock must also be a function.
Call jest.mock('./mocked-class.js');, and also import './mocked-class.js'.
For any class methods you want to track calls to, create a variable that points to a mock function, like this: const mockedMethod = jest.fn();. Use those in the next step.
Call MockedClass.mockImplementation(). Pass in an arrow function that returns an object containing any mocked methods, each set to its own mock function (created in step 2).
The same thing can be done using manual mocks (__mocks__ folder) to mock ES6 classes. In this case, the exported mock is created by calling jest.fn().mockImplementation(), with the same argument described in (3) above. This creates a mock function. In this case, you'll also need to export any mocked methods you want to spy on.
The same thing can be done by calling jest.mock('mocked-class.js', factoryFunction), where factoryFunction is again the same argument passed in 3 and 4 above.
An example is worth a thousand words, so here's the code.
Also, there's a repo demonstrating all of this, here:
https://github.com/jonathan-stone/jest-es6-classes-demo/tree/mocks-working
First, for your code
if you were to add the following setup code, your tests should pass:
const connectMock = jest.fn(); // Lets you check if `connect()` was called, if you want
Socket.mockImplementation(() => {
return {
connect: connectMock
};
});
(Note, in your code: Socket.mock.calls[0][1] should be [0][0], and [0][2] should be [0][1]. )
Next, a contrived example
with some explanation inline.
mocked-class.js. Note, this code is never called during the test.
export default class MockedClass {
constructor() {
console.log('Constructed');
}
mockedMethod() {
console.log('Called mockedMethod');
}
}
mocked-class-consumer.js. This class creates an object using the mocked class. We want it to create a mocked version instead of the real thing.
import MockedClass from './mocked-class';
export default class MockedClassConsumer {
constructor() {
this.mockedClassInstance = new MockedClass('yo');
this.mockedClassInstance.mockedMethod('bro');
}
}
mocked-class-consumer.test.js - the test:
import MockedClassConsumer from './mocked-class-consumer';
import MockedClass from './mocked-class';
jest.mock('./mocked-class'); // Mocks the function that creates the class; replaces it with a function that returns undefined.
// console.log(MockedClass()); // logs 'undefined'
let mockedClassConsumer;
const mockedMethodImpl = jest.fn();
beforeAll(() => {
MockedClass.mockImplementation(() => {
// Replace the class-creation method with this mock version.
return {
mockedMethod: mockedMethodImpl // Populate the method with a reference to a mock created with jest.fn().
};
});
});
beforeEach(() => {
MockedClass.mockClear();
mockedMethodImpl.mockClear();
});
it('The MockedClassConsumer instance can be created', () => {
const mockedClassConsumer = new MockedClassConsumer();
// console.log(MockedClass()); // logs a jest-created object with a mockedMethod: property, because the mockImplementation has been set now.
expect(mockedClassConsumer).toBeTruthy();
});
it('We can check if the consumer called the class constructor', () => {
expect(MockedClass).not.toHaveBeenCalled(); // Ensure our mockClear() is clearing out previous calls to the constructor
const mockedClassConsumer = new MockedClassConsumer();
expect(MockedClass).toHaveBeenCalled(); // Constructor has been called
expect(MockedClass.mock.calls[0][0]).toEqual('yo'); // ... with the string 'yo'
});
it('We can check if the consumer called a method on the class instance', () => {
const mockedClassConsumer = new MockedClassConsumer();
expect(mockedMethodImpl).toHaveBeenCalledWith('bro');
// Checking for method call using the stored reference to the mock function
// It would be nice if there were a way to do this directly from MockedClass.mock
});
For me this kind of Replacing Real Class with mocked one worked.
// Content of real.test.ts
jest.mock("../RealClass", () => {
const mockedModule = jest.requireActual(
"../test/__mocks__/RealClass"
);
return {
...mockedModule,
};
});
var codeTest = require("../real");
it("test-real", async () => {
let result = await codeTest.handler();
expect(result).toMatch(/mocked.thing/);
});
// Content of real.ts
import {RealClass} from "../RealClass";
export const handler = {
let rc = new RealClass({doing:'something'});
return rc.realMethod("myWord");
}
// Content of ../RealClass.ts
export class RealClass {
constructor(something: string) {}
async realMethod(input:string) {
return "The.real.deal "+input;
}
// Content of ../test/__mocks__/RealClass.ts
export class RealClass {
constructor(something: string) {}
async realMethod(input:string) {
return "mocked.thing "+input;
}
Sorry if I misspelled something, but I'm writing it on the fly.
I have one event emitter (a transform stream) and that event emitter basically has a bunch of child emitters.
I want to forward events from all the child emitters to the parent emitter, something like this:
const EE = require('events');
const exportEvents = new EE();
const sumanEvents = Transform(); // create new transform stream (an event emitter)
sumanEvents.on('test', function () {
exportEvents.emit.bind(exportEvents, 'test').apply(exportEvents, arguments);
});
sumanEvents.on('error', function () {
exportEvents.emit.bind(exportEvents, 'error').apply(exportEvents, arguments);
});
sumanEvents.on('suman-test-file-complete', function () {
exportEvents.emit.bind(exportEvents, 'suman-test-file-complete').apply(exportEvents, arguments);
});
Basically, from what I can tell, I have forwarded the error, test, and suman-test-file-complete events to the parent, but this seems pretty ugly.
Is there at least a more sophisticated way of doing it? I assume there is not a way to directly inherit events with Node.js 'events' package so I am not asking about that.
You could override sumanEvents.emit() so you could see any event that was emitted and then grab it and echo it to exportEvents:
(function(origEmitter, forwardEmitter) {
// save original .emit method
let oldEmit = origEmitter.emit;
// assign override
origEmitter.emit = function() {
// allow the event to be normally emitted
oldEmit.apply(origEmitter, arguments);
// then forward it to the forwardEmitter
forwardEmitter.emit.apply(forwardEmitter, arguments);
};
})(sumanEvents, exportEvents);
Or, put into a reusable function form so you can use it on more than one emitter without copying the code:
function forwardEmitter(origEmitter, forwardEmitter) {
// save original .emit method
let oldEmit = origEmitter.emit;
// assign override
origEmitter.emit = function() {
// allow the event to be normally emitted
oldEmit.apply(origEmitter, arguments);
// then forward it to the forwardEmitter
forwardEmitter.emit.apply(forwardEmitter, arguments);
};
}
forwardEmitter(sumanEvents, exportEvents);
This functionality could be encapsulated in a new object that derives from EventEmitter if you wanted to more easily be able to reuse it, but that would only work if you control the creation of the first EventEmitter object so you could make it your special derived object that supported forwarding of all messages. The example above can be "added on" to any existing regular EventEmitter object.
There's also an EventEmitter2 object described here that allows you to use wildcards to register event handlers so you could register an interest in all events or a subset of events that way without naming each one individually.
And, there's a discussion of this concept on a Google mailing list: https://groups.google.com/forum/#!topic/nodejs-dev/TzRPxsHf0FA
but this seems pretty ugly.
Is there a reason you're binding and applying? It seems like these are being used incorrectly. Lines like exportEvents.emit.bind(exportEvents, 'test').apply(exportEvents, arguments) can be simplified to EE.emit.apply(exportEvents, 'test', arguments) or, using spread syntax, exportEvents.emit('test', ...arguments)
That being said, a utility function might be helpful here. jfriend00's above solution is ok, but I don't really like that it overrides a method on the original object (even if the original method is being called).
The other solution actually works out to be simpler (fewer lines of code):
function forwardEvent(emitterToListenOn, emitterToEmitOn, event) {
emitterToListenOn.on(event, function() { //can't use arrow here, as we need 'arguments'
emitterToEmitOn.emit(event, arguments);
});
}
function forwardEvents(emitterToListenOn, emitterToEmitOn, events) {
events.forEach(event => forwardEvent(emitterToListenOn, emitterToEmitOn, event));
}
function forwardEventsToMultipleEmitters(emitterToListenOn, emittersToEmitOn, events) {
emittersToEmitOn.forEach(emitterToEmitOn => forwardEvents(emitterToListenOn, emitterToEmitOn, events));
}
For those that aren't comfortable with arrow functions, here's the same code without:
function forwardEvent(emitterToListenOn, emitterToEmitOn, event) {
emitterToListenOn.on(event, function() { //can't use arrow here, as we need 'arguments'
emitterToEmitOn.emit(event, arguments);
});
}
function forwardEvents(emitterToListenOn, emitterToEmitOn, events) {
events.forEach(function(event) {
forwardEvent(emitterToListenOn, emitterToEmitOn, event);
});
}
function forwardEventsToMultipleEmitters(emitterToListenOn, emittersToEmitOn, events) {
emittersToEmitOn.forEach(function(emitterToEmitOn) {
forwardEvents(emitterToListenOn, emitterToEmitOn, events);
});
}
To use these in your original code,
const EE = require('events');
const exportEvents = new EE();
const sumanEvents = Transform(); // create new transform stream (an event emitter)
forwardEvents(sumanEvents, exportEvents, ['test', 'error', 'suman-test-file-complete']);
This forwardEvent is fewer lines of code than jfriend00's forwardEmitter and doesn't override any functions and the final solution looks pretty clean to me.
I need to use jQuery events on non-DOM-related objects.
This works fine:
var o = {
}
$(o).on('bump', function () {
alert('ouch')
})
$(o).trigger('bump')
http://jsfiddle.net/d35bf35y/
But instead I need to attach an event on a property... the following code does not work.
var o = {
prop: 'test'
}
// Bind an event handler
$(o.prop).on('bump', function () {
alert('ouch')
})
// Trigger an event
$(o.prop).trigger('bump')
http://jsfiddle.net/d35bf35y/1/
In my real application that property will have an object.
I would like to know if is possible use jQuery in this way or an alternative solution.
"In my real application that property will have an object."
That part is rather important. This works:
var o = {
prop: {}
}
// Bind an event handler
$(o.prop).on('bump', function () {
alert('ouch')
})
// Trigger an event
$(o.prop).trigger('bump')
You can attach one on the Object and everytime it fires, you run a function that does something with your properties. (check if they're the same, or else)
var o = {
prop: 'test'
};
$(o).on('bump', function (e) {
var props = e.delegateTarget;
// do what you gotta do, like check if something has changed from before.
})
$(o).trigger('bump')
Or if your property is also an object, it should work.
When it comes to spying on jQuery functions (e.g. bind, click, etc) it is easy:
spyOn($.fn, "bind");
The problem is when you want to spy on $('...') and return defined array of elements.
Things tried after reading other related answers on SO:
spyOn($.fn, "init").andReturn(elements); // works, but breaks stuff that uses jQuery selectors in afterEach(), etc
spyOn($.fn, "merge").andReturn(elements); // merge function doesn't seem to exist in jQuery 1.9.1
spyOn($.fn, "val").andReturn(elements); // function never gets called
So how do I do this? Or if the only way is to spy on init function how do I "remove" spy from function when I'm done so afterEach() routing doesn't break.
jQuery version is 1.9.1.
WORKAROUND:
The only way I could make it work so far (ugly):
realDollar = $;
try {
$ = jasmine.createSpy("dollar").andReturn(elements);
// test code and asserts go here
} finally {
$ = realDollar;
}
Normally, a spy exists for the lifetime of the spec. However, there's nothing special about destroying a spy. You just restore the original function reference and that's that.
Here's a handy little helper function (with a test case) that will clean up your workaround and make it more usable. Call the unspy method in your afterEach to restore the original reference.
function spyOn(obj, methodName) {
var original = obj[methodName];
var spy = jasmine.getEnv().spyOn(obj, methodName);
spy.unspy = function () {
if (original) {
obj[methodName] = original;
original = null;
}
};
return spy;
}
describe("unspy", function () {
it("removes the spy", function () {
var mockDiv = document.createElement("div");
var mockResult = $(mockDiv);
spyOn(window, "$").and.returnValue(mockResult);
expect($(document.body).get(0)).toBe(mockDiv);
$.unspy();
expect(jasmine.isSpy($)).toEqual(false);
expect($(document.body).get(0)).toBe(document.body);
});
});
As an alternative to the above (and for anyone else reading this), you could change the way you're approaching the problem. Instead of spying on the $ function, try extracting the original call to $ to its own method and spying on that instead.
// Original
myObj.doStuff = function () {
$("#someElement").css("color", "red");
};
// Becomes...
myObj.doStuff = function () {
this.getElements().css("color", "red");
};
myObj.getElements = function () {
return $("#someElement");
};
// Test case
it("does stuff", function () {
spyOn(myObj, "getElements").and.returnValue($(/* mock elements */));
// ...
});
By spying on the window itself you have access to any window properties.
As Jquery is one of these you can easily mock it as below and return the value you require.
spyOn(window, '$').and.returnValue(mockElement);
Or add a callFake with the input if it needs to be dynamic.