Skip to content

Commit 5c82336

Browse files
committed
Allow replacing a service by a mocked one.
1 parent 0354896 commit 5c82336

File tree

8 files changed

+151
-5
lines changed

8 files changed

+151
-5
lines changed

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
],
1313
"main": "index.js",
1414
"scripts": {
15-
"bdd": "mocha --colors --reporter=dot --watch --recursive tests",
16-
"coverage": "istanbul cover node_modules/.bin/_mocha --report lcovonly -- --reporter=dot --recursive tests",
17-
"coverage-upload": "cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
15+
"bdd": "NODE_ENV=test mocha --colors --reporter=dot --watch --recursive tests",
16+
"coverage": "NODE_ENV=test istanbul cover node_modules/.bin/_mocha --report lcovonly -- --reporter=dot --recursive tests",
17+
"coverage-upload": "NODE_ENV=test cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js",
1818
"lint": "eslint --color .",
19-
"test": "mocha --colors --reporter=dot --recursive tests"
19+
"test": "NODE_ENV=test mocha --colors --reporter=dot --recursive tests"
2020
},
2121
"repository": {
2222
"type": "git",

src/Container.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ var Container = function Container(serviceDefinitionCollection, parameterCollect
1818
};
1919

2020
/**
21+
* Return a parameter value.
22+
*
2123
* @param {String} name
2224
* @return {*|null}
2325
*/
@@ -32,6 +34,8 @@ Container.prototype.getParameter = function (name) {
3234
};
3335

3436
/**
37+
* Return a sercice instance.
38+
*
3539
* @param {String} name
3640
* @return {*}
3741
*/
@@ -53,4 +57,24 @@ Container.prototype.getService = function (name) {
5357
return serviceInstance;
5458
};
5559

60+
if (process.env.NODE_ENV === 'test') {
61+
/**
62+
* Replace a service instance by a mocked instance. Only avialable in test environement.
63+
*
64+
* @param {String} name
65+
* @param {Function} mock
66+
*/
67+
Container.prototype.mockService = function (name, mock) {
68+
if (!this.serviceDefinitionCollection.hasServiceDefinition(name)) {
69+
throw new Error('Unknown service "' + name + '".');
70+
}
71+
72+
if (!this.serviceStorage.hasInstance(name)) {
73+
this.serviceStorage.addInstance(name, mock);
74+
return;
75+
}
76+
77+
this.serviceStorage.replaceInstance(name, mock);
78+
};
79+
}
5680
module.exports = Container;

src/ServiceStorage.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@ ServiceStorage.prototype.addInstance = function (name, instance) {
2727
this.instances[name] = instance;
2828
};
2929

30+
/**
31+
* @param {String} name
32+
* @param {*} instance
33+
*/
34+
ServiceStorage.prototype.replaceInstance = function (name, instance) {
35+
if (!this.hasInstance(name)) {
36+
throw new Error('There is no service to replace with name the "' + name + '".');
37+
}
38+
39+
this.instances[name] = instance;
40+
};
41+
3042
/**
3143
* @param {String} name
3244
* @return {*}

tests/functionals/ContainerFactorySpec.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ var servicesConfigurationErroredWithUnknownDependencies = require('./../fixture/
88
var ServiceA = require('./../fixture/valid/ServiceA');
99

1010
describe('ContainerFactory', function () {
11+
var previousProcessEnv = process.env.NODE_ENV;
12+
13+
afterEach(function () {
14+
process.env.NODE_ENV = previousProcessEnv;
15+
});
16+
1117
it('should return the parameter value', function () {
1218
var container = ContainerFactory.create(servicesConfigurationValid.services, servicesConfigurationValid.parameters);
1319

tests/functionals/ContainerSpec.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
var expect = require('chai').expect;
4+
var sinon = require('sinon');
45
var Container = require('./../../src/Container');
56
var Parameter = require('./../../src/Parameter');
67
var ParameterCollection = require('./../../src/ParameterCollection');
@@ -137,4 +138,23 @@ describe('Container', function () {
137138
expect(serviceC.bar).to.be.equal(42);
138139
expect(serviceC.serviceA).to.be.equal(serviceA);
139140
});
141+
142+
it('should replace a service by a mocked one', function () {
143+
var serviceDefinitionCollection = sinon.createStubInstance(ServiceDefinitionCollection);
144+
serviceDefinitionCollection.hasServiceDefinition.withArgs('foo').returns(true);
145+
146+
var parameterCollection = sinon.createStubInstance(ParameterCollection);
147+
148+
var fakeServiceMock = sinon.spy();
149+
150+
var container = new Container(serviceDefinitionCollection, parameterCollection);
151+
container.mockService('foo', fakeServiceMock);
152+
153+
expect(container.getService('foo')).to.be.equals(fakeServiceMock);
154+
expect(fakeServiceMock).to.not.have.been.called;
155+
});
156+
157+
it.skip('should replace a cached service by a mocker one', function () {
158+
// TODO
159+
});
140160
});

tests/units/ContainerSpec.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict';
2+
3+
var expect = require('chai').expect;
4+
var sinon = require('sinon');
5+
var Container = require('./../../src/Container');
6+
var ServiceDefinitionCollection = require('./../../src/ServiceDefinitionCollection');
7+
var ParameterCollection = require('./../../src/ParameterCollection');
8+
9+
describe('Container', function () {
10+
var previousProcessEnv = process.env.NODE_ENV;
11+
12+
afterEach(function () {
13+
process.env.NODE_ENV = previousProcessEnv;
14+
});
15+
16+
/**
17+
* @see http://stackoverflow.com/a/16060619
18+
* @param {[type]} module
19+
* @return {[type]}
20+
*/
21+
function requireUncached(module) {
22+
delete require.cache[require.resolve(module)]
23+
return require(module)
24+
}
25+
26+
it('should throw a error when trying to mock an undefined service', function () {
27+
var serviceDefinitionCollection = sinon.createStubInstance(ServiceDefinitionCollection);
28+
var parameterCollection = sinon.createStubInstance(ParameterCollection);
29+
30+
var container = new Container(serviceDefinitionCollection, parameterCollection);
31+
32+
expect(function () {
33+
container.mockService('foo', function () {});
34+
}).to.throw('Unknown service "foo".');
35+
});
36+
37+
it('should provide an api to mock a service in test environement', function () {
38+
var serviceDefinitionCollection = sinon.createStubInstance(ServiceDefinitionCollection);
39+
var parameterCollection = sinon.createStubInstance(ParameterCollection);
40+
41+
process.env.NODE_ENV = 'test';
42+
var FreshContainer = requireUncached('./../../src/Container');
43+
44+
var container = new FreshContainer(serviceDefinitionCollection, parameterCollection);
45+
expect(container).to.have.property('mockService');
46+
});
47+
48+
it('should not provide an api to mock a service in production environement', function () {
49+
var serviceDefinitionCollection = sinon.createStubInstance(ServiceDefinitionCollection);
50+
var parameterCollection = sinon.createStubInstance(ParameterCollection);
51+
52+
process.env.NODE_ENV = 'production';
53+
var FreshContainer = requireUncached('./../../src/Container');
54+
55+
var container = new FreshContainer(serviceDefinitionCollection, parameterCollection);
56+
expect(container).to.not.have.property('mockService');
57+
});
58+
});

tests/units/ServiceStorageSpec.js

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ var expect = require('chai').expect;
44
var ServiceStorage = require('./../../src/ServiceStorage');
55

66
describe('ServiceStorage', function () {
7-
it('should add an instance to the storage and return it', function () {
7+
it('should add an instance to the storage and return it', function () {
88
var serviceStorage = new ServiceStorage();
99

1010
var someInstance = Object.create({a: 'bar'});
@@ -43,4 +43,26 @@ describe('ServiceStorage', function () {
4343
expect(serviceStorage.hasInstance('foo')).to.be.true;
4444
expect(serviceStorage.hasInstance('bar')).to.be.false;
4545
});
46+
47+
it('should replace an instance to the storage and return it', function () {
48+
var serviceStorage = new ServiceStorage();
49+
50+
var someInstance = Object.create({a: 'bar'});
51+
var someOthenInstance = Object.create({b: 'baz'});
52+
53+
serviceStorage.addInstance('foo', someInstance);
54+
serviceStorage.replaceInstance('foo', someOthenInstance);
55+
56+
expect(serviceStorage.getInstance('foo')).to.equals(someOthenInstance);
57+
});
58+
59+
it('should throw an exception if there is no instance to replace', function () {
60+
var serviceStorage = new ServiceStorage();
61+
62+
var someInstance = Object.create({a: 'bar'});
63+
64+
expect(function () {
65+
serviceStorage.replaceInstance('foo', someInstance);
66+
}).to.throw('There is no service to replace with name the "foo".');
67+
});
4668
});

tests/world.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,7 @@ var chai = require("chai");
44
var sinonChai = require("sinon-chai");
55

66
chai.use(sinonChai);
7+
8+
if (process.env.NODE_ENV !== 'test') {
9+
console.warn('Warning: you\'re not running the tests in test environment. Define the NODE_ENV variable to "test".');
10+
}

0 commit comments

Comments
 (0)