Testing Ember.Logger.error assertions - ember.js

I'm using Ember.Logger.error:
if (isInvalid) {
Ember.Logger.error('this is invalid');
}
I want to test it in qunit:
assert.throws(() => myFunction(), /this is invalid/, 'error was thrown');
But assert.throws doesn't catch the error. It does if I replace Ember.Logger.error with a simple throw statement, but surely there's a way to test for logged Ember errors. Anyone know the way?
UPDATE:
I made a little addon that adds this ability to QUnit. You can get it here.

Okay, so I've done a research how it's done in Ember and I've seen what's the practice to test it:
ember.js/packages/ember/tests/helpers/link_to_test.js
ember.js/packages/ember/tests/routing/basic_test.js
Here's example test function you could use to test calling Ember.Logger.error in helper unit test:
/* global Ember */
import { demo } from '../../../helpers/demo';
import { module, test } from 'qunit';
module('Unit | Helper | demo');
test('catching log', function(assert) {
assert.expect(1); // define how many assertions we expect
const oldError = Ember.Logger.error; // store original error function in variable
Ember.Logger.error = function(message) { // monkey patch with our custom function
assert.equal(message, 'this is invalid', 'error was thrown'); // our assertion
};
demo(); // actually call function
Ember.Logger.error = oldError; // restore original error function
});

Related

jest.fn() v/s jest.mock()?

For mocking uuidv4, I am using this :
import { v4 as uuidv4 } from "uuid";
jest.mock("uuid");
uuidv4.mockReturnValue("uuid123");
And for mocking window.confirm, I am using this:
window.confirm = jest.fn().mockImplementation(() => true);
These both are working all right.
But when i try doing this i.e
const uuidv4 = jest.fn.mockImplementation(() => "uuid123");
I get this error
TypeError: jest.fn.mockImplementation is not a function
I am confused between jest.fn() and jest.mock().
Can someone please elaborate on which one to use and when to use, with suitable example?
Just a quick explanation for you:
jest.mock is to mock a certain module. Once you write jest.mock('uuid') then it means all exported things would be turned to a jest.Mock type, that's why you can mock v4 method: v4.mockReturnValue('yourV4Id');
jest.mock('aModule');
import {aMember} from "aModule";
// is now a jest mock type <=> jest.fn()
aMember.mockReturnValue('a value');
jest.fn is a function which returns a jest.Mock type which can be considered as an function to create whatever you want:
const aMock = jest.fn().mockReturnValue(1) // <=> const aMock = () => 1;
// The difference is jest mock type can be used to assert then. Most of cases is to check
// whether it gets called or not

QUnit returns error in strict mode

I'm trying to create a (pure) constructor function and a QUnit test for it:
//app.js
function myFunc(param1, param2) {
this.param1 = param1;
this.param2 = param2;
return this;
}
//test.js
QUnit.test("should return a string and a number", function(assert) {
assert.ok(myFunc("some-string", 4545435234), "a string and a number were returned");
});
The code runs and passes my test until I add "use strict" to app.js. Then QUnit displays the following fail message:
1. Died on test #1 at http://some/url/:1:1: Cannot set property 'param1' of undefined
Source: TypeError: Cannot set property 'param1' of undefined
I can get both the code to work and the test to pass if I return the myFunc parameters as an array:
function myFunc(param1, param2)) {
return [param1, param2];
}
But that just doesn't seem right. I get that this has something to do with var hoisting but I'm not clear about it.
Thanks in advance.
...
In strict mode JavaScript functions are not given the default context (this), thus you must provide the context. Once way to do this is through the new keyword. If you change your assertion to the following I think this will work:
assert.ok(new myFunc("some-string", 4545435234), "a string and a number were returned");

Testing redux-saga takeEvery

I have the following super simple Redux-Saga that I want to test with Jest.
function* nextApi() {
yield* takeEvery(
(action) => !!(action.meta && action.meta.next),
nextApiSaga
)
}
I've looked at Redux-Sagas-Test-Plan, but that only seems to allow you to unit test functions that contain Saga Effect Creators and doesn't seem to support Saga Helpers. There is also Redux-Saga-Test but that just does a deepEqual on the yielded effect and doesn't test the arrow function.
What I want to be able to do is past the following two objects to takeEvery and see that nextApiSaga is only called in the second case.
{ type: 'foo' }
{ type: 'foo', meta: { next: 'bar' } }
I left you a comment about redux-saga-test-plan having methods for saga helpers, but you can easily test takeEvery with it. Call testSaga with your saga and then invoke the takeEvery method assertion with the pattern (note I keep a reference to your original anonymous function) and the other saga.
const helper = action => !!(action.meta && action.meta.next)
function* nextApi() {
yield* takeEvery(
helper,
nextApiSaga
)
}
testSaga(nextApi).takeEvery(helper, nextApiSaga)
Taking a different approach, I came up with this. Not sure if it's the best answer, but it seems to work. Adding here in case anyone else has the same problem and still open to better suggestions.
function getTakeEveryFunction(saga) {
return saga().next().value.TAKE.pattern
}
it('takes actions with meta.next property', () => {
const func = getTakeEveryFunction(nextApi)
expect(func({ type:'foo' })).toBe(false)
expect(func({ type:'foo', meta: { next: 'bar' } })).toBe(true)
})

How to unit test an ember controller

I have a single action defined in an ember controller that calls 2 separate functions that are part of the controller. I'd like to mock out those functions in a unit test in order to confirm if the action method called the correct function.
My controller looks like this:
export default Ember.Controller.extend({
functionA() {
return;
},
functionB() {
return;
},
actions: {
actionMethod(param) {
if(param) {
return this.functionA();
}
else {
return this.functionB();
}
}
}
});
In practice, the controller works, however in the unit test, functionA and functionB are both undefined. I tried to log this to the console, but can't find where functionA and functionB are, so I'm unable to properly mock them. I expected them to be in the top level of the object next to actions, but instead I only found _actions with actionMethod properly defined.
My unit test looks like below
const functionA = function() { return; }
const functionB = function() { return; }
test('it can do something', function(assert) {
let controller = this.subject();
// I don't want the real functions to run
controller.set('functionA', functionA);
controller.set('functionB', functionB);
controller.send('actionMethod', '');
// raises TypeError: this.functionA is not a function
// this doesn't work etiher
// controller.functionB = functionB;
// controller.functionA = functionA;
// controller.actions.actionMethod();
}
Does anyone have any ideas on how I can replace those functions in the testing environment? Or perhaps, is there a better way to test this functionality or set up my controller?
edit
typo: this.subject to this.subject()
To replace the functions of the controller in the unit test, you can pass parameter to the this.subject() function:
let controller = this.subject({
functionA(){
//this function overriddes functionA
},
functionB(){
//this function overriddes functionB
},
});
Look at the sample twiddle.
This method is especially useful for replacing the injected service of the controllers.
Introduce corresponding property you are dealing with, let us say name property,
So your controllers would be looking like this,
import Ember from 'ember';
export default Ember.Controller.extend({
name:'',
functionA() {
this.set('name','A');
},
functionB() {
this.set('name','B');
},
actions: {
actionMethod(param) {
if(param) {
return this.functionA();
}
else {
return this.functionB();
}
}
}
});
And you can test for the name property value after calling actionMethod.
test(" testing functionA has been called or not", function(assert){
let controller = this.subject();
controller.send('actionMethod',true);
//If you would like to call functionA just say controller.functionA()
assert.strictEqual(controller.get('name'),'A',' name property has A if actionMethod arguments true');
controller.send('actionMethod',false);
assert.strictEqual(controller.get('name'),'B',' name property has B actionMethod arguments false');
});

Attempted to wrap sendRequest which is already wrapped

I'm writing unit tests using Jasmine for my Backbone cache and am trying to mock up a function response using Sinon.js. For different tests, I expect different things to happen so I am creating a mock before each test and deleting it after, then filling in the expects behavior within the test itself. However, I'm getting an error and the tests are failing.
Here's my spec with just the relevant tests (other tests aren't using mock):
describe("mysite.core.Cache.XFooInfo", function() {
var fnMock;
beforeEach(function() {
fnMock = sinon.mock(fn);
});
afterEach(function() {
delete fnMock;
});
it("should make a request after function fooCreated called", function() {
fnMock.expects("sendRequest").once().withExactArgs("ModuleFoo", "getFoo", ["1000"]);
events.trigger("fooCreated", [{Args:[test.data.XFooInfo]}]);
fnMock.verify();
});
});
describe("mysite.core.Cache.XFooBarInfo", function() {
var fnMock;
beforeEach(function() {
fnMock = sinon.mock(fn);
});
afterEach(function() {
delete fnMock;
});
it("should make a request after function booUpdated called", function() {
var booCopy = $.extend(true, {}, test.data.XBooInfo);
booCopy[0].Args[0].FooID = "12345";
fnMock.expects("sendRequest").once().withExactArgs("ModuleFoo", "getFoo", ["12345"]);
events.trigger("booUpdated", booCopy);
fnMock.verify();
});
});
The first test works fine and passes. The second test, however, gives me this error:
TypeError: Attempted to wrap sendRequest which is already wrapped
at Object.wrapMethod (https://localhost:8443/mysite/web/tests/libs/sinon-1.7.1.js:528:23)
at Object.expects (https://localhost:8443/mysite/web/tests/libs/sinon-1.7.1.js:2092:27)
at null.<anonymous> (https://localhost:8443/mysite/web/shasta-cList-tests/spec/CacheSpec.js:909:15)
at jasmine.Block.execute (https://localhost:8443/mysite/web/tests/libs/jasmine-1.2.0.rc3/jasmine.js:1024:15)
at jasmine.Queue.next_ (https://localhost:8443/mysite/web/tests/libs/jasmine-1.2.0.rc3/jasmine.js:2025:31)
at jasmine.Queue.start (https://localhost:8443/mysite/web/tests/libs/jasmine-1.2.0.rc3/jasmine.js:1978:8)
at jasmine.Spec.execute (https://localhost:8443/mysite/web/tests/libs/jasmine-1.2.0.rc3/jasmine.js:2305:14)
at jasmine.Queue.next_ (https://localhost:8443/mysite/web/tests/libs/jasmine-1.2.0.rc3/jasmine.js:2025:31)
at jasmine.Queue.start (https://localhost:8443/mysite/web/tests/libs/jasmine-1.2.0.rc3/jasmine.js:1978:8)
at jasmine.Suite.execute (https://localhost:8443/mysite/web/tests/libs/jasmine-1.2.0.rc3/jasmine.js:2450:14)
I can't find anything in the Sinon.js docs to tell me what I'm doing wrong. I know verify also does a restore on all the functions it mocks and I thought that it was enough to allow me to write a new expects behavior for the same function, but apparently I was wrong.
What is the right way to do this?
Ok, so after spending 2 hours on this problem last Friday with no luck, I figured it out twenty min after posting this question. Adding var fn.sendRequest = function(module, fnName, args) {}; to the beforeEach function fixed all the failing tests.
this one works too, less to type:
var fn.sendRequest = function () { };