I'm unit testing JavaScript with Jasmine and I am running into some problems.
I have a large file to test and it has a lot of dependencies and those dependencies have their own dependencies. Because of said dependencies I want to mock all I can. There lies the problem. How can I mock a constructor so that it includes the methods that belong to it?
Lets say I'm testing a method createMap of class Map:
In that createMap method it calls for Layers class constructor using
var layers = new Layers()
I'm spying on it using
spyOn(window, 'Layers').and.callThrough()
That works fine but later in the createMap method it calls for layers.addLayer() where addLayer is a method of Layers class. Problem is that because I mocked the Layers call it doesn't recognize the addLayer method.
Is there a way to mock it so that it includes all the methods of the called class or is my only option to stub the whole Layers class or not mock it?
Or what would be a good way to handle this? I've tried to spyOn(Layers, 'addLayer') but there it says that no method addLayer is found.
I'm sorry if it's confusing a bit. I had trouble thinking how should I ask it.
IMO, it's unnecessary to spy on window, since you can easily shadow the variable in local scope by creating a spy object with the same name:
describe('Map', function () {
var Layers;
beforeEach(function () {
Layers = function () {
// alternatively, you could move this to Layers.prototype
this.addLayers = jasmine.createSpy('Layers#addLayers');
};
});
/* ... */
});
If you want an automatic mocking and using CommonJS modules, you may try Jest framework, which is built on top of Jasmine.
Let's talk in terms of example classes you have provided.
You're writing a test suite for Map. All its dependencies (in example we have only Layer) MUST be mocked. Because in a unit test you're supposed to test one layer, as small functionality as possible. It means that you should provide such a mocked Layer constructor that exposes interface used in Map. For example:
function Layers() {
this.addLayer = sinon.spy();
}
In this test suite only Map class should remain "real". I.e. it's code must not be altered. And with such mockups like Layer you make sure that you do not trigger any interaction with real-code dependencies (own-written dependencies should be tested in a different test suite, also make sure you don't try to test framework functions, like $tate.resolve, $inject etc.). If class Map is complicated and has multiple dependencies, investigate sinon features that help automate this process, for example sinon.mock
If you ever transpile class syntax to a es3 or another pre-2015 dialect you will discover something interesting.
class a {
constructor(){
...
}
index()
{
...
}
}
Becomes:
var a = /** #class */ (function () {
function a() {
...
}
a.prototype.index = function () {
...
};
return a;
}());
This same implementation is used by later standards but masked by the 2015 class syntax. In other words a.index doesn't exist instead it's defined as a.prototype.index. Thus you need spyOn(a.prototype, 'index') to spy on it.
Change spyOn(Layers, 'addLayer') to spyOn(Layers.prototype, 'addLayer')
Related
I am gradually improving a codebase that originally had some AngularJs in various versions and some code that was not in a framework at all using various versions of a software API. (For some reason this API is available - to pages loaded through the application - on AngularJS's $window.external...go figure.)
In my pre-ES6, AngularJs 1.8 phase, I have three services that interact with the software's API (call them someAPIget, someAPIset, and someAPIforms). Something like this:
// someAPIget.service.js
;(function () {
var APIget = function ($window, helperfunctions) {
function someFunc (param) {
// Do something with $window.external.someExternalFunc
return doSomethingWith(param)
}
return {
someFunc: someFunc
}
}
angular.module('someAPIModule').factory('someAPIget', ['$window', 'helperfunctions', someAPIget])
})()
I then had a service and module a level up from this, with someAPIModule as a dependency, that aggregated these functions and passed them through under one name, like this:
// apiinterface.service.js
;(function () {
// Change these lines to switch which API service all functions will use.
var APIget = 'someAPIget'
var APIset = 'someAPIset'
var APIforms = 'someAPIforms'
var APIInterface = function (APIget, APIset, APIforms) {
return {
someFunc: APIget.someFunc,
someSettingFunc: APIset.someSettingFunc,
someFormLoadingFunc: APIforms.someFormLoadingFunc
}
}
angular.module('APIInterface').factory('APIInterface', [APIget, APIset, APIforms, APIInterface])
})()
I would then call these functions in various other controllers and services by using APIInterface.someFunc(etc). It worked fine, and if we switch to a different software provider, we can use our same pages without rewriting everything, just the interface logic.
However, I'm trying to upgrade to Typescript and ES6 so I can use import and export and build some logic accessible via command line, plus prepare for upgrading to Angular 11 or whatever the latest version is when I'm ready to do it. So I rebuilt someAPIget to a class:
// someAPIget.service.ts
export class someAPIget {
private readonly $window
private readonly helperfunctions
static $inject = ['$window', 'helperfunctions']
constructor ($window, helperfunctions) {
this.$window = $window
this.helperfunctions = helperfunctions
}
someFunc (param) {
// Do something with this.$window.external.someExternalFunc
return doSomethingWith(param)
}
}
}
angular
.module('someAPImodule')
.service('someAPIget', ['$window', 'helperfunctions', someAPIget])
Initially it seemed like it worked (my tests still pass, or at least after a bit of cleanup in the Typescript compilation department they do), but then when I load it into the live app... this.$window is not defined. If, however, I use a direct dependency and call someAPIget.someFunc(param) instead of through APIInterface.someFunc(param) it works fine (but I really don't want to rewrite thousands of lines of code using APIInterface for the calls, plus it will moot the whole point of wrapping it in an interface to begin with). I've tried making APIInterface into a class and assigning getters for every function that return the imported function, but $window still isn't defined. Using console.log statements I can see that this.$window is defined inside someFunc itself, and it's defined inside the getter in APIInterface, but from what I can tell when I try to call it using APIInterface it's calling it without first running the constructor on someAPIget, even if I make sure to use $onInit() for the relevant calls.
I feel like I am missing something simple here. Is there some way to properly aggregate and rename these functions to use throughout my program? How do alias them correctly to a post-constructed version?
Edit to add: I have tried with someAPIget as both a factory and a service, and APIInterface as both a factory and a service, and by calling APIInterface in the .run() of the overall app.module.ts file, none of which works. (The last one just changes the location of the undefined error.)
Edit again: I have also tried using static for such a case, which is somewhat obviously wrong, but then at least I get the helpful error highlight in VSCode of Property 'someProp' is used before its initialization.ts(2729).
How exactly are you supposed to use a property that is assigned in the constructor? How can I force AngularJS to execute the constructor before attempting to access the class's members?
I am not at all convinced that I found an optimal or "correct" solution, but I did find one that works, which I'll share here in case it helps anyone else.
I ended up calling each imported function in a class method of the same name on the APIInterface class, something like this:
// apiinterface.service.ts
// Change these lines to switch which API service all functions will use.
const APIget = 'someAPIget'
const APIset = 'someAPIset'
const APIforms = 'someAPIforms'
export class APIInterface {
private readonly APIget
private readonly APIset
private readonly APIforms
constructor (APIget, APIset, APIforms) {
this.APIget = APIget
this.APIset = APIset
this.APIforms = APIforms
}
someFunc(param: string): string {
return this.APIget.someFunc(param)
}
someSettingFunc(param: string): string {
return this.APIset.someSettingFunc(param)
}
someFormLoadingFunc(param: string): string {
return this.APIforms.someFormLoadingFunc(param)
}
}
angular
.module('APIInterface')
.factory('APIInterface', [APIget, APIset, APIforms, APIInterface])
It feels hacky to me, but it does work.
Later Update:
I am now using Angular12, not AngularJS, so some details may be a bit different. Lately I have been looking at using the public-api.ts file that Angular12 generates to accomplish the same thing (ie, export { someAPIget as APIget } from './filename' but have not yet experimented with this, since it would still require either consolidating my functions somehow or rewriting the code that consumes them to use one of three possible solutions. It would be nice not to have to duplicate function signatures and doc strings however. It's still a question I'm trying to answer more effectively, I will update again if I find something that really works.
I'm getting rather confused as to if something is possible or not.
I create a module that contains the following:
export function logText(){
console.log('some text');
}
export class Example {
constructor(){
logText();
}
}
The intent is for the user to call new Example to start off the module logic.
import { logText, Example } from 'example';
// Do some magic here to modify the functionality of logText
new Example();
Is it possible for the end user to modify logText?
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
I frequently see repos with lots of functions exported that are useless without the users having to remake almost all the functionality manually, making it rather pointless to do. One good example is this repo whre theuy even call the exported functions their 'API'. In that example these are rather pointless exports and at worse would just cause issues if someone tried to use them in conjunction with the main function. But if you could modify them and have them still run then it would make sense to me.
Given this:
import { logText, Example } from 'example';
Is it possible for the end user to modify logText?
Since you aren't being very specific about what you mean by "modify logText", I'll go through several options:
Can you reassign some other function to the variable logText?
No. You cannot do that. When you use import, it creates a variable that is const and cannot be assigned to. Even if it wasn't const, it's just a local symbol that wouldn't affect the other module's use of its logText() anyway. The import mechanism is designed this way on purpose. A loader of your module is not supposed to be able to replace internal implementation pieces of the module that weren't specifically designed to be replaced.
Can you modify the code inside of the logText function from outside of the module that contains it?
No, you cannot. The code within modules lives inside it's own function scope which gives it privacy. You cannot modify code within that module from outside the module.
Can you replace the logText() function inside the module such that the implementation of Example inside that class will use your logText() function?
No, you cannot do that from outside the module. You would have to actually modify the module's code itself or someone would have to design the Example interface to have a replaceable or modifiable logText() function that the Example object used.
For example, logText() could be made a method on Example and then you could override it with your own implementation which would cause Example's implementation to use your override.
Code in the module that you do not modify:
export class Example {
constructor(){
this.logText();
}
logText() {
console.log('some text');
}
}
Code doing the import:
import { Example } from 'example';
class MyExample extends Example {
constructor() {
super();
}
logText() {
console.log("my own text");
}
}
let o = new MyExample();
Can you create your own version of logText and use it locally?
Sure, you can do that.
function myLogText() {
do your own thing
}
And, you could even NOT import logText so that you could use the symbol name logText() locally if you wanted. But, that won't affect what Example does at all.
Are there ways to design the example module so that that logText() can be easily replaced.
Yes, there are lots of ways to do that. I showed one above that makes logText a method that can be overriden. It could also be passed as an optional argument to the Example constructor.
There could even be an exported object that allowed the caller to replace properties on that object. For example:
export const api = {
logText: function logText(){
console.log('some text');
}
};
export class Example {
constructor(){
api.logText();
}
}
Then, use it like this:
import { api, Example } from 'example';
api.logText = function() {
console.log('my Text');
};
I would generally not recommend this because it sets you up for usage conflicts between multiple users of the same module where each one tries to modify it globally in ways that conflict with each other. The subclassing model (mentioned above) lets each use of the module customize in its own way without conflicting with other usages of the module.
Is it possible for the end user to modify logText?
No, that's not possible, import bindings are immutable, and function objects are basically immutable wrt the code they contain.
It would make sense for there to be a way for users to do something like this, or they'd have to take the entire module into their own repo just to make small functionality tweaks.
Why not make the log function an optional argument in the constructor? Usually when something is variable it becomes a parameter.
export class Example {
constructor(log=logText){
log();
}
}
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);
So I'm writing a whole bunch of vendor-specific files in node which all have a similar controller pattern, so it makes sense for me to cut them out and put into a common file.
You can see my common controller file here: https://gist.github.com/081a04073656bf28f46b
Now when I use them in my multiple modules, each consecutively loaded module is overwriting the first. This is because the file is only required once and passed dynamically through to each module on load (this allows me to add extra modules and these modules are able to add their own routes, for example). You can see an example module here: https://gist.github.com/2382bf93298e0fc58599
You can see here on line 53 I've realised that we need to create a seperate instance every time, so I've tried to create a new instance by copying the standardControllers object into a new object, then initialising the new object. This has zero impact on the code, and the code behaves in exactly the same way.
Any ideas guys? I'm in a bit of a jam with this one!
First thing I'd do is try to make things simpler and reduce coupling by invoking the single responsibility principle, et al.
http://www.codinghorror.com/blog/2007/03/curlys-law-do-one-thing.html
Put those Schemas into their own files, eg
models/client.js
models/assistant.js
models/contact.js
I've also found that embedded docs + mongoose is generally a PITA. I'd probably promote all those to top level docs.
You don't need to enclose your object's keys in quotes.
routes = {
list: function() {} // no quotes is aok
}
Also 'list' in typical REST apps is called 'index'. Anyway.
Ok, I'd break this up differently. Since you're requiring stuff from the index.js file in the middleware, they become tightly coupled, which is bad. in fact, I think I'd rewrite this whole thing so it was tidier. Sorry.
I'd probably replace your 'middleware' file with an express-resource controller
https://github.com/visionmedia/express-resource (built by author of express). This is a good framework for restful controllers, such as what you're building. The auto-loader is really sweet.
You may also want to look at: http://mcavage.github.com/node-restify/ It's new, I haven't tried it out, but I've heard good things.
Since what you're building is basically an automated mongoose-crud system, with optional overriding, I'd create an express-resource controller as your base
/controllers/base_controller.js
and it might look like
var BaseController = function() {} // BaseController constructor
BaseController.prototype.index = function() {
// copy from your middleware
}
BaseController.prototype.show = function() {
// copy from your middleware
}
BaseController.prototype.create = function() {
// copy from your middleware
}
// etc
module.exports = BaseController
Then I'd do something like:
/controllers/some_resource_controller.js
which might look something like:
var BaseController = require('./base_controller')
var NewResourceController = function() {
// Apply BaseController constructor (i.e. call super())
BaseController.apply(this, arguments)
}
NewResourceController.prototype = new Base()
NewResourceController.prototype.create = function() {
// custom create method goes here
}
module.exports = NewResourceController
Then to use it, you can do:
var user = app.resource(myResourceName, new ResourceController());
…inside some loop which sets myResourceName to be whatever crud you're trying to set up.
Here's some links for you to read:
http://tobyho.com/2011/11/11/js-object-inheritance/
http://yehudakatz.com/2011/08/12/understanding-prototypes-in-javascript/
Also, it sounds like you're not writing tests. Write tests.
http://www.codinghorror.com/blog/2006/07/i-pity-the-fool-who-doesnt-write-unit-tests.html
How much can I stretch RequireJS to provide dependency injection for my app? As an example, let's say I have a model that I want to be a singleton. Not a singleton in a self-enforcing getInstance()-type singleton, but a context-enforced singleton (one instance per "context"). I'd like to do something like...
require(['mymodel'], function(mymodel) {
...
}
And have mymodel be an instance of the MyModel class. If I were to do this in multiple modules, I would want mymodel to be the same, shared instance.
I have successfully made this work by making the mymodel module like this:
define(function() {
var MyModel = function() {
this.value = 10;
}
return new MyModel();
});
Is this type of usage expected and common or am I abusing RequireJS? Is there a more appropriate way I can perform dependency injection with RequireJS? Thanks for your help. Still trying to grasp this.
This is not actually dependency injection, but instead service location: your other modules request a "class" by a string "key," and get back an instance of it that the "service locator" (in this case RequireJS) has been wired to provide for them.
Dependency injection would involve returning the MyModel constructor, i.e. return MyModel, then in a central composition root injecting an instance of MyModel into other instances. I've put together a sample of how this works here: https://gist.github.com/1274607 (also quoted below)
This way the composition root determines whether to hand out a single instance of MyModel (i.e. make it singleton scoped) or new ones for each class that requires it (instance scoped), or something in between. That logic belongs neither in the definition of MyModel, nor in the classes that ask for an instance of it.
(Side note: although I haven't used it, wire.js is a full-fledged dependency injection container for JavaScript that looks pretty cool.)
You are not necessarily abusing RequireJS by using it as you do, although what you are doing seems a bit roundabout, i.e. declaring a class than returning a new instance of it. Why not just do the following?
define(function () {
var value = 10;
return {
doStuff: function () {
alert(value);
}
};
});
The analogy you might be missing is that modules are equivalent to "namespaces" in most other languages, albeit namespaces you can attach functions and values to. (So more like Python than Java or C#.) They are not equivalent to classes, although as you have shown you can make a module's exports equal to those of a given class instance.
So you can create singletons by attaching functions and values directly to the module, but this is kind of like creating a singleton by using a static class: it is highly inflexible and generally not best practice. However, most people do treat their modules as "static classes," because properly architecting a system for dependency injection requires a lot of thought from the outset that is not really the norm in JavaScript.
Here's https://gist.github.com/1274607 inline:
// EntryPoint.js
define(function () {
return function EntryPoint(model1, model2) {
// stuff
};
});
// Model1.js
define(function () {
return function Model1() {
// stuff
};
});
// Model2.js
define(function () {
return function Model2(helper) {
// stuff
};
});
// Helper.js
define(function () {
return function Helper() {
// stuff
};
});
// composition root, probably your main module
define(function (require) {
var EntryPoint = require("./EntryPoint");
var Model1 = require("./Model1");
var Model2 = require("./Model2");
var Helper = require("./Helper");
var entryPoint = new EntryPoint(new Model1(), new Model2(new Helper()));
entryPoint.start();
});
If you're serious about DI / IOC, you might be interested in wire.js: https://github.com/cujojs/wire
We use a combination of service relocation (like Domenic describes, but using curl.js instead of RequireJS) and DI (using wire.js). Service relocation comes in very handy when using mock objects in test harnesses. DI seems the best choice for most other use cases.
Not a singleton in a self-enforcing getInstance()-type singleton, but
a context-enforced singleton (one instance per "context").
I would recommend it only for static objects. It's perfectly fine to have a static object as a module that you load using in the require/define blocks. You then create a class with only static properties and functions. You then have the equivalent of the Math Object that has constants like PI, E, SQRT and functions like round(), random(), max(), min(). Great for creating Utility classes that can be injected at any time.
Instead of this:
define(function() {
var MyModel = function() {
this.value = 10;
}
return new MyModel();
});
Which creates an instance, use the pattern for a static object (one where values are always the same as the Object never gets to be instantiated):
define(function() {
return {
value: 10
};
});
or
define(function() {
var CONSTANT = 10;
return {
value: CONSTANT
};
});
If you want to pass an instance (the result of using a Module that have return new MyModel();), then, within an initialize function, pass a variable that capture the current state / context or pass on the Object that contains information on state / context that your modules needs to know about.