Mock/Stub constructor - javascript

I have the following code:
class Clients
constructor : ->
#clients = []
createClient : (name)->
client = new Client name
#clients.push client
I am testing it with Jasmine BDD like this:
describe 'Test Constructor', ->
it 'should create a client with the name foo', ->
clients = new clients
clients.createClient 'Foo'
Client.should_have_been_called_with 'Foo'
it 'should add Foo to clients', ->
clients = new clients
clients.createClient 'Foo'
expect(clients.clients[0]).toEqual SomeStub
In my first test I want to check if the constructor is being called with the correct name. In my second I just want to confirm that whatever came out of new Client was added to the array.
I am using Jasmine BDD and it has a way to create spies/mocks/stubs but it seems it's not possible to test constructor. So I am looking into a way to test the constructor it would be nice if there is a way that I don't need an extra library but I am open to anything.

It is possible to stub out constructors in Jasmine, the syntax is just a bit unexpected:
spy = spyOn(window, 'Clients');
In other words, you don't stub out the new method, you stub out the class name itself in the context where it lives, in this case window. You can then chain on a andReturn() to return a fake object of your choosing, or a andCallThrough() to call the real constructor.
See also: Spying on a constructor using Jasmine

I think the best plan here is to pull out the creation of the new Client object to a separate method. This will allow you to test the Clients class in isolation and use mock Client objects.
I've whipped up some example code, but I haven't tested it with Jasmine. Hopefully you can get the gist of how it works:
class Clients
constructor: (#clientFactory) ->
#clients = []
createClient : (name)->
#clients.push #clientFactory.create name
clientFactory = (name) -> new Client name
describe 'Test Constructor', ->
it 'should create a client with the name foo', ->
mockClientFactory = (name) ->
clients = new Clients mockClientFactory
clients.createClient 'Foo'
mockClientFactory.should_have_been_called_with 'Foo'
it 'should add Foo to clients', ->
someStub = {}
mockClientFactory = (name) -> someStub
clients = new Clients mockClientFactory
clients.createClient 'Foo'
expect(clients.clients[0]).toEqual someStub
The basic plan is to now use a separate function (clientFactory) to create new Client objects. This factory is then mocked in the tests allowing you to control exactly what is returned, and inspect that it has been called correctly.

My solution ended up similar to #zpatokal
I ended up using a module accross my app (not really big app), and mocking from there. One catch is that and.callThrough won't work, as the constructor is going to be called from the Jasmine method, so I had to do some trickery with and.callFake.
On unit.coffee
class PS.Unit
On units.coffee
class PS.Units
constructor: ->
new PS.Unit
And on the spec files:
Unit = PS.Unit
describe 'Units', ->
it 'should create a Unit', ->
spyOn(PS, 'Unit').and.callFake -> new Unit arguments... # and.callThrough()
expect(PS.Unit).toHaveBeenCalled()

Clearer solution with recent jasmine version:
window.Client = jasmine.createSpy 'Client'
clients.createClient 'Foo'
expect(window.Client).toHaveBeenCalledWith 'Foo'

Related

Test Meteor server method calling in client code with authenticated users

In a Meteor app, I need to test some client code that has statements such as
Meteor.call('foo', param1, param2, (error, result) => { .... });
And, in these methods, I have security checks to make sure that the method can only be called by authenticated users. However, all these tests fail during tests because no user is authenticated.
In each server methods, I check users like this
if (!Roles.userIsInRole(this.userId, [ ...roles ], group)) {
throw new Meteor.Error('restricted', 'Access denied');
}
I have read that we should directly export the server methods and test them directly, and I actually do this for server methods testing, but it is not possible, here, since I need to test client code that depend on Meteor.call.
I also would certainly not want to have if (Meteor.isTest || Meteor.isAppTest) { ... } all over the place....
I thought perhaps wrapping my exported methods like this :
export default function methodsWrapper(methods) {
Object.keys(methods).forEach(method => {
const fn = methods[method];
methods[method] = (...args) => {
const user = Factory.create('user', { roles: { 'default': [ 'admin' ] } });
return fn.call({ userId: user._id }, ...args);
};
});
};
But it only works when calling the methods directly.
I'm not sure how I can test my client code with correct security validations. How can I test my client code with authenticated users?
Part I: Making the function an exported function
You just need to add the exported method also to meteor methods.
imports/api/foo.js
export const foo = function(param1, param2){
if (!Roles.userIsInRole(this.userId, [ ...roles ], group)) {
throw new Meteor.Error('restricted', 'Access denied');
}
//....and other code
};
This method can then be imported in your server script:
imports/startup/methods.js
import {foo} from '../api/foo.js'
Meteor.methods({
'foo' : foo
});
So it is available to be called via Mateor.call('foo'...). Note that the callback has not to be defined in foo's function header, since it is wrapped automatically by meteor.
imports/api/foo.tests.js
import {foo} from './foo.js'
if (Meteor.isServer) {
// ... your test setup
const result = foo(...) // call foo directly in your test.
}
This is only on the server, now here is the thing for testing on the client: you will not come around calling it via Meteor.call and test the callback result. So on your client you still would test like:
imports/api/foo.tests.js
if (Meteor.isClient) {
// ... your test setup
Meteor.call('foo', ..., function(err, res) {
// assert no err and res...
});
}
Additional info:
I would advice you to use mdg:validated-method which allows the same functionality above PLUS gives you more sophisticated control over method execution, document schema validation and flexibility. It is also documented well enough to allow you to implement your above described requirement.
See: https://github.com/meteor/validated-method
Part II: Running you integration test with user auth
You have two options here to test your user authentication. They have both advantages and disadvantages and there debates about what is the better approach. No mater which one of both you will test, you need to write a server method, that adds an existing user to given set of roles.
Approach 1 - Mocking Meteor.user() and Meter.userid()
This is basically described/discussed in the following resources:
A complete gist example
An example of using either mdg:validated-method or plain methods
Using sinon spy and below also an answer from myself by mocking it manually but this may not apply for your case because it is client-only. Using sinon requires the following package: https://github.com/practicalmeteor/meteor-sinon
Approach 2 - Copying the "real" application behavior
In this case you completely test without mocking anything. You create real users and use their data in other tests as well.
In any case you need a server method, that creates a new user by given name and roles. Note that it should only be in a file with .test.js as name. Otherwise it can be considered a risk for security.
/imports/api/accounts/accounts.tests.js
Meteor.methods({
createtestUser(name,password, roles, group);
const userId = Accounts.createUser({username:name, password:password});
Roles.addUserToRoles(userId, roles, group);
return userId;
});
Note: I often heard that this is bad testing, which I disagree. Especially integration testing should mime the real behavior as good as possible und should use less mocking/spying as unit tests do.

Coffeescript create new instance of a class given an object of that class

In coffeescript, I have an object created from a class. Later, I no longer have access to that class, but I would like to create a new instance. For example:
# MySingleton.coffee
class MySingleton
module.exports = new MySingleton
and then:
# MySingletonTests.coffee
mySingleton = require './MySingleton'
# testSingleton = ???
# I would like to create a new MySingleton here
I think I would like to use something like JS's Object.create here, but if I call Object.create(mySingleton) it seems to just make a reference to the old object, and mySingleton.prototype is undefined
EDIT:
I know I could just export the class in this example, but I was hoping not to because it is a singleton in production code and I would like to make sure it doesn't get new'd, but in my unit testing I would like to make new copies of it so state doesn't persist between tests
It turns out new myInstance.constructor() does exactly what I wanted to do.

Class and scope in ember js

Im building an ember application consuming a couple of web services. And I'm trying to pass a class object throw the config/environment file doing this:
var myclass = require('myclass-package');
var ENV = {
APP: {
MY_OBJ_CLASS: new myclass({
//CONSTRUCTOR PARAMS...
PROP1: "HELLO"
})
}
}
In my ember app/controllers I'm doing this:
import ENV from '../config/environment';
var obj1 = ENV.APP.MY_OBJ_CLASS;
I can see that the object is instantiated if I console.log the class object but when I try to access to the properties and functions, I can't and I get back this error:
var data = obj1.my_function_class({param1:1});
console.log(data)
TypeError: obj1.my_function_class is not a function
But the function exist...
What is the way to access to my class properties and functions?
config/environment.js is a special file. It is executed in Node, then serialized to be made available for the browser app.
You should not store any functionality in that file.
Put your class into a proper Ember module. Depending on what you're trying to achieve, that could be a service, a model, an util, etc.
Provide more details on your original problem, not your attempted solution. See http://xyproblem.info .

stubbing an entire class for testing in sinon

Preamble: I've read lots of of SO and blog posts, but haven't seen anything that answers this particular question. Maybe I'm just looking for the wrong thing...
Suppose I'm developing a WidgetManager class that will operate on Widget objects.
How do I use sinon to test that WidgetManager is using the Widget API correctly without pulling in the whole Widget library?
Rationale: The tests for a WidgetManager should be decoupled from the Widget class. Perhaps I haven't written Widget yet, or perhaps Widget is an external library. Either way, I should be able to test that WidgetManager is using Widget's API correctly without creating real Widgets.
I know that sinon mocks can only work on existing classes, and as far as I can tell, sinon stubs also need the class to exist before it can be stubbed.
To make it concrete, how would I test that Widget.create() is getting called exactly once with a single argument 'name' in the following code?
code under test
// file: widget-manager.js
function WidgetManager() {
this.widgets = []
}
WidgetManager.prototype.addWidget = function(name) {
this.widgets.push(Widget.create(name));
}
testing code
// file: widget-manager-test.js
var WidgetManager = require('../lib/widget-manager.js')
var sinon = require('sinon');
describe('WidgetManager', function() {
describe('#addWidget', function() {
it('should call Widget.create with the correct name', function() {
var widget_manager = new WidgetManager();
// what goes here?
});
it('should push one widget onto the widgets list', function() {
var widget_manager = new WidgetManager();
// what setup goes here?
widget_manager.addWidget('fred');
expect(widget_manager.widgets.length).to.equal(1);
});
});
Aside: Of course, I could define a MockWidget class for testing with the appropriate methods, but I'm more interested in really learning how to use sinon's spy / stub / mock facilities correctly.
The answer is really about dependency injection.
You want to test that WidgetManager is interacting with a dependency (Widget) in the expected way - and you want freedom to manipulate and interrogate that dependency. To do this, you need to inject a stub version of Widget at testing time.
Depending on how WidgetManager is created, there are several options for dependency injection.
A simple method is to allow the Widget dependency to be injected into the WidgetManager constructor:
// file: widget-manager.js
function WidgetManager(Widget) {
this.Widget = Widget;
this.widgets = [];
}
WidgetManager.prototype.addWidget = function(name) {
this.widgets.push(this.Widget.create(name));
}
And then in your test you simply pass a stubbed Widget to the WidgetManager under test:
it('should call Widget.create with the correct name', function() {
var stubbedWidget = {
create: sinon.stub()
}
var widget_manager = new WidgetManager(stubbedWidget);
widget_manager.addWidget('fred');
expect(stubbedWidget.create.calledOnce);
expect(stubbedWidget.create.args[0] === 'fred');
});
You can modify the behaviour of your stub depending on the needs of a particular test. For example, to test that the widget list length increments after widget creation, you can simply return an object from your stubbed create() method:
var stubbedWidget = {
create: sinon.stub().returns({})
}
This allows you to have full control over the dependency, without having to mock or stub all methods, and lets you test the interaction with its API.
There are also options like proxyquire or rewire which give more powerful options for overriding dependencies at test time. The most suitable option is down to implementation and preference - but in all cases you are simply aiming to replace a given dependency at testing time.
Your addWidget method does 2 things:
"converts" a string to a Widget instance;
adds that instance to internal storage.
I suggest you change addWidget signature to accept instance directly, instead of a name, and move out creation some other place. Will make testing easier:
Manager.prototype.addWidget = function (widget) {
this.widgets.push(widget);
}
// no stubs needed for testing:
const manager = new Manager();
const widget = {};
manager.addWidget(widget);
assert.deepStrictEquals(manager.widgets, [widget]);
After that, you'll need a way of creating widgets by name, which should be pretty straight-forward to test as well:
// Maybe this belongs to other place, not necessarily Manager class…
Manager.createWidget = function (name) {
return new Widget(name);
}
assert(Manager.createWidget('calendar') instanceof Widget);

How to Jasmine Spy on private member variables in Node?

I have a node module with a function constructor. I want to spy on a private property within that module but can't figure out how to spy on it. As soon as I create a closure around it I can't see it anymore from the test. The code...
//logger.js
function Logger(){
this.log(str){...}
}
//mainMethod.js
var Logger = require('Logger');
function mainMethod(){
var logger = new Logger();
logger.log('error')
}
exports.mainMethod = mainMethod;
//my test
it('when main method is called then logger will log "error"',function(){
spyOn( ... ?)
expect(logger.log).toHaveBeenCalledWith('error');
}}
Clearly the test does not work. So I don't really know how to spy on that logger. I can't use a singleton since each node request must use a new object. If this was C# I would simply use dependency injection and then spy on the interface. But everywhere I read that node does not need a DI container. Please can someone help?

Categories