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 () { };
Related
My code makes network calls. Sometimes these fail and I've implemented some retry logic. Now I wish to test this with a unit test. I'm using Mockito for this. I'm having trouble mocking this particular method call, this is my test code:
var mock = MockHttpClient();
var answers = <Future<String>>[
Future.error(Exception('boom')),
Future.value('some return value'),
];
when(mock.getStuff()).thenAnswer((_) => answers.removeAt(0));
var client = Client(inner: mock);
var result = client.someCall(); // This method call uses the `inner` object to make the `getStuff` network call.
expect(result, 'some return value');
Running this code throws an exception. And I get that because of the first returned Future! Mockito has the appropriate method call named thenThrow but I can't figure out how to combine the two.
I've figured it out. The trick is not to use a Mock but a Fake.
That looks like this:
class FakeHttpClient extends Fake implements HttpClient {
final List<Future<List<String>>> answers = [
Future.error(Exception('boom')),
Future.value('some return value'),
];
#override
Future<String> getStuff() {
return answers.removeAt(0);
}
}
Then use an instance of FakeHttpClient as an argument to the original example's Client:
var fake = FakeHttpClient();
var client = Client(inner: fake);
var result = client.someCall(); // This method call uses the `inner` object to make the `getStuff` network call.
expect(result, 'some return value');
I want to spyOn a promise and fake that promise in my unit test but the problem is that if I run first the contructor that the problem that he first run the promise and then run the Spyon.
But when i first run the spyOn and then the constructor it gives a error that storage is undefined.
Does someone know how to fix this?
Spec file:
describe('Settings Service', () => {
beforeEach(() => {
settingsService = new SettingsService(); // This gives a error beceause it runs the promise
spyOn(settingsService.storage, 'get').and.callFake((key: String): Promise<string> => {
return new Promise((resolve, reject) => { resolve('url'); });
});
});
constructor:
constructor() {
this.storage = new Storage(LocalStorage);
this.storage.get('url').then(data => {
this.setLink(data);
});
}
UPDATE:
I tried also this:
let injector: any = ReflectiveInjector.resolveAndCreate([SettingsService]);
settingsService = injector.get(SettingsService);
spyOn(settingsService.storage, 'get').and.callFake((key: String): Promise<string> => {
return new Promise((resolve, reject) => { resolve('https://secure.info/pascal'); });
});
The problem you have is that you are instantiating Storage within the constructor, so you have no access to it from the outside. That means that you cannot mock it.
Setting spyOn before calling settingsService = new SettingsService(); doesn't work either because the field storage has not been created yet.
You have two ways to solve this:
Mocking the service $httpBackend using the following code. Take a look at this post as an example
beforeEach(inject(function($injector) {
service = $injector.get('carService');
$httpBackend = $injector.get('$httpBackend');
$httpBackend.when('GET', "/api/cars/types").respond(["Toyota", "Honda", "Tesla"]);
}));
This way you can mock the promise you get when calling this.storage.get('url') and test the behaviour.
Making Storage a service and injecting it mocked: If you use this approach you could moke Storage and therefore mock the behaviour of this.storage.get('url'). The code of your class `` would look like this
static $inject = ['Storage'];
constructor(storage: Storage) {
this.storage = storage;
this.storage.get('url').then(data => {
this.setLink(data);
});
}
But this way depends on how do you define and use Storage so generally the first way will be better
I am trying to test this function with Jasmine:
myFunc: function () {
var name = document.getElementById("bob");
display.innerHTML = name;
return 100;
}
The test fails with ReferenceError: document is not defined. I understand that I'm not running the test in a browser so document isn't defined. But how come mocking it out isn't working? Is there a way to write this test with standalone Jasmine only?
How would one generally go about testing these kinds of functions (JavaScript mixed with DOM)? I'd prefer not to use another library/framework.
// Mock, stub dom object.
var document;
beforeEach(function () {
document = jasmine.createSpyObj('document', ['getElementById', 'createElement']);
});
describe("myFunc", function () {
it("return 100", function () {
console.log(document); // My mock document object successfully logs.
expect(myFunc()).toEqual(100); // but document isn't defined in here.
});
});
In Ember, I sometimes run into the situation where I need to check if a function is returning a promise. For example, if I have a route that is derived:
MyRoute = ParentRoute.extend({
beforeModel: function() {
this._super().then(function() {
// do something...
});
}
});
But although beforeModel can return a promise, it might not. In particular, if it's the default Ember.K implementation, then it doesn't. I'd rather not always be doing:
var res = this._super();
if (typeof res.then === "function") {
res.then(function() {
// do X
});
} else {
// do X
}
I assume there's a way to wrap something that one doesn't know if it's a thenable, and then chain regardless. But I couldn't find it in the documentation.
The above is undesirable because it's verbose, and requires having the code for X twice.
Thoughts?
Update:
I was able to confirm #torazaburo's response with the following coffeescript test:
`import { test, module } from 'ember-qunit'`
module "testing promise behavior"
test "can cast to promise", ->
expect 3
order = []
returnsPromise = ->
new Ember.RSVP.Promise (resolve) ->
order.push 'a'
resolve('response 1')
returnsValue = ->
order.push 'b'
'response 2'
Ember.run ->
Ember.RSVP.resolve(returnsPromise()).then (response) ->
order.push 'c'
equal response, 'response 1'
Ember.RSVP.resolve(returnsValue()).then (response) ->
order.push 'd'
equal response, 'response 2'
equal order.join(' '), 'a b c d'
Thanks for the solution! It seems RSVP's implementation of Promises also has a built in resolve method that does what you suggest, and it turns out to be the same as cast, as you suggest, althought that is now deprecated.
There may be other better ways to do this, but you could do:
function ensurePromise(x) {
return new Ember.RSVP.Promise(function(resolve) {
resolve(x);
});
}
If x is not a promise, then this returns a promise which is already fulfilled with that value, which you can then hang then's off of. If x is a promise, then it returns a promise which assumes its status (including resolved/rejected status and value/reason).
This is equivalent in native Promises to
Promise.resolve(x)
So in your case,
MyRoute = ParentRoute.extend({
beforeModel: function() {
ensurePromise(this._super()).then(function() {
// do something...
});
}
});
Note, however, that this will potentially turn a synchronous value into an asynchronous value (a promise). However, it is generally considered bad practice to have functions that behave synchronously in some cases and asynchronously in others. So it seems OK, in the case we have a value which is potentially either synchronous or asynchronous, to coerce it into something which is always asynchronous.
I believe in some past version there used to be something called RSVP.Promise.cast, which I seem to recall did roughly the same thing, but I can't track it down now.
I realize there have been several questions similar to this, but none of those answers seems to be resolving my issue. My objective is to take a list of language's, and filter them so that my template can display a subset of the full list.
I started off by verifying that my computed property is working:
MyController.js
// Works as expected
languagesFiltered: function() {
return this.get('languages');
}.property('languages')
Then I added in a filter function, but here's where I ran into trouble:
MyController.js
languagesFiltered: function() {
// console.log shows that languages is actually a promise
var languages = this.get('languages');
// all of this returns a promise, but Handlebars can't handle the promise
return languages.then( function( languagesArray ) {
return languagesArray.filter( function( item, index, enumerable) {
return item.get('name') !== 'English';
});
})
}.property('languages')
I'm attempting to use the Ember.Array.filter method (http://emberjs.com/api/classes/Ember.ArrayProxy.html#method_filter). The filter seems to be working correctly, but now languagesFiltered returns a promise, and Handlebars can't handle that.
I tried one last alternative:
MyController.js
languagesFiltered: function() {
var languages = this.get('languages');
// "return languages;" works
// But "return languages.filter" returns an empty array
return languages.filter( function( item, index, enumerable ) {
console.log(item);
return true;
});
}.property('languages')
And console.log(item) never gets called. So my questions are:
What is the best way to implement the simple filter I'm after?
This is a read-only computed property, but what are best practices for handling async values in computed properties?
I'm using Ember 1.7.0-beta4, Ember Data 1.0.0-beta10, and ember-cli 0.44. I'd upgrade to Ember 1.7.0, but there's a small bug that affects another part of our app, so we're waiting until 1.7.1. Thanks for your input!
You can try returning a PromiseArray instead of just the promise.
You should be able to do something like..
languagesFiltered: function() {
// all of this returns a promise, but Handlebars can't handle the promise
var promise = this.get('languages').then( function( languagesArray ) {
return languagesArray.filter( function( item, index, enumerable) {
return item.get('name') !== 'English';
});
})
return DS.PromiseArray.create({
promise: promise
});
}.property('languages')
Now there are few better solutions. I use ember-promise-helpers.
So can keep your languagesFiltered code intact and do the following inside your hbs:
{{#each (await languagesFiltered) as|lang|}}
...
...
More – https://emberigniter.com/guide-promises-computed-properties/