@e22m4u/js-service
Version:
Реализация принципа инверсии управления для JavaScript
1,036 lines (960 loc) • 39.4 kB
JavaScript
import {expect} from 'chai';
import {Service} from './service.js';
import {format} from '@e22m4u/js-format';
import {createSpy} from '@e22m4u/js-spy';
import {
ServiceContainer,
SERVICE_CONTAINER_CLASS_NAME,
} from './service-container.js';
describe('ServiceContainer', function () {
it('should expose static property "kinds"', function () {
expect(ServiceContainer.kinds).to.be.eql([SERVICE_CONTAINER_CLASS_NAME]);
const MyContainer = class extends ServiceContainer {};
expect(MyContainer.kinds).to.be.eql([SERVICE_CONTAINER_CLASS_NAME]);
});
describe('constructor', function () {
it('should not require any arguments', function () {
const res = new ServiceContainer();
expect(res).to.be.instanceof(ServiceContainer);
});
it('should set the first argument as the parent container', function () {
const parent = new ServiceContainer();
const container = new ServiceContainer(parent);
const res = container['_parent'];
expect(res).to.be.eq(parent);
});
it('should require the parameter "parent" to be an instance of ServiceContainer', function () {
const throwable = v => () => new ServiceContainer(v);
const error = v =>
format(
'Parameter "parent" must be an instance ' +
'of ServiceContainer, but %s given.',
v,
);
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable('')).to.throw(error('""'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(0)).to.throw(error('0'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable([])).to.throw(error('Array'));
expect(throwable({})).to.throw(error('Object'));
throwable(undefined)();
throwable(null)();
throwable(new ServiceContainer())();
});
});
describe('getParent', function () {
it('should throw an error if no parent container', function () {
const container = new ServiceContainer();
const throwable = () => container.getParent();
expect(throwable).to.throw('Service container has no parent.');
});
it('should return parent container', function () {
const parent = new ServiceContainer();
const container = new ServiceContainer(parent);
const res = container.getParent();
expect(res).to.be.eq(parent);
});
});
describe('hasParent', function () {
it('should return true if a parent container exists and false otherwise', function () {
const container1 = new ServiceContainer();
const parent = new ServiceContainer();
const container2 = new ServiceContainer(parent);
expect(container1.hasParent()).to.be.false;
expect(container2.hasParent()).to.be.true;
});
});
describe('get', function () {
it('should require the parameter "ctor" to be a Function', function () {
const container = new ServiceContainer();
const throwable = v => () => container.get(v);
const error = v =>
format(
'Parameter "ctor" must be a class constructor, but %s given.',
v,
);
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable(undefined)).to.throw(error('undefined'));
expect(throwable(null)).to.throw(error('null'));
expect(throwable([])).to.throw(error('Array'));
expect(throwable({})).to.throw(error('Object'));
});
describe('Service', function () {
it('should pass itself and given arguments to the given constructor', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
const service = container.get(MyService, 'foo', 'bar');
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('should instantiate from an existing factory function', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('should override an existing factory function', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService, 'baz', 'qux');
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
it('should cache a new instance', function () {
let executed = 0;
class MyService extends Service {
constructor(container) {
super(container);
++executed;
}
}
const container = new ServiceContainer();
const service1 = container.get(MyService);
const service2 = container.get(MyService);
expect(service1).to.be.instanceof(MyService);
expect(service2).to.be.instanceof(MyService);
expect(service1).to.be.eq(service2);
expect(executed).to.be.eq(1);
});
it('should override the cached instance when arguments provided', function () {
let executed = 0;
const givenArgs = [];
class MyService extends Service {
constructor(container, arg) {
super(container);
++executed;
givenArgs.push(arg);
}
}
const container = new ServiceContainer();
const service1 = container.get(MyService, 'foo');
const service2 = container.get(MyService);
const service3 = container.get(MyService, 'bar');
const service4 = container.get(MyService);
expect(service1).to.be.instanceof(MyService);
expect(service2).to.be.instanceof(MyService);
expect(service3).to.be.instanceof(MyService);
expect(service4).to.be.instanceof(MyService);
expect(service1).to.be.eq(service2);
expect(service2).to.be.not.eq(service3);
expect(service3).to.be.eq(service4);
expect(executed).to.be.eq(2);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
describe('class inheritance', function () {
it('should create an instance of the requested parent class even when its child class is registered', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ChildService);
const service = container.get(ParentService);
expect(service).to.be.instanceof(ParentService);
});
it('should create an instance of the requested child class even when its parent class is registered', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
const service = container.get(ChildService);
expect(service).to.be.instanceof(ChildService);
});
it('should return an instance of the requested child class instead of an existing instance of its parent class', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
const service1 = container.get(ParentService);
expect(service1).to.be.instanceof(ParentService);
const service2 = container.get(ChildService);
expect(service2).to.be.instanceof(ChildService);
expect(service2).to.be.not.eq(service1);
});
it('should prioritize the given registered constructor even its child class is registered too', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
container.add(ChildService);
const res1 = container.get(ParentService);
const res2 = container.get(ChildService);
expect(res1).to.be.instanceOf(ParentService);
expect(res2).to.be.instanceOf(ChildService);
expect(res1).to.be.not.eq(res2);
});
it('should prioritize the given registered constructor even its parent class is registered too', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
container.add(ChildService);
const res1 = container.get(ChildService);
const res2 = container.get(ParentService);
expect(res1).to.be.instanceOf(ChildService);
expect(res2).to.be.instanceOf(ParentService);
expect(res1).to.be.not.eq(res2);
});
});
describe('in case of a parent container', function () {
it('should instantiate in the parent container', function () {
class MyService extends Service {}
const parent = new ServiceContainer();
parent.add(MyService);
const child = new ServiceContainer(parent);
const res = child.get(MyService);
expect(res).to.be.instanceof(MyService);
});
it('should return an instance from the parent container', function () {
class MyService extends Service {}
const parent = new ServiceContainer();
const service = parent.get(MyService);
const child = new ServiceContainer(parent);
const res = child.get(MyService);
expect(res).to.be.eq(service);
});
it('should not add the given constructor to the parent container', function () {
class MyService extends Service {}
const parent = new ServiceContainer();
const child = new ServiceContainer(parent);
const service = child.get(MyService);
expect(service).to.be.instanceof(MyService);
const res1 = child.has(MyService);
const res2 = parent.has(MyService);
expect(res1).to.be.true;
expect(res2).to.be.false;
});
});
});
describe('non-Service', function () {
it('should pass given arguments to the given constructor', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
const service = container.get(MyService, 'foo', 'bar');
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('should instantiate from an existing factory function', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('should override an existing factory function', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService, 'baz', 'qux');
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
it('should cache a new instance', function () {
let executed = 0;
class MyService {
constructor() {
++executed;
}
}
const container = new ServiceContainer();
const service1 = container.get(MyService);
const service2 = container.get(MyService);
expect(service1).to.be.instanceof(MyService);
expect(service2).to.be.instanceof(MyService);
expect(service1).to.be.eq(service2);
expect(executed).to.be.eq(1);
});
it('should override the cached instance when arguments provided', function () {
let executed = 0;
const givenArgs = [];
class MyService {
constructor(arg) {
++executed;
givenArgs.push(arg);
}
}
const container = new ServiceContainer();
const service1 = container.get(MyService, 'foo');
const service2 = container.get(MyService);
const service3 = container.get(MyService, 'bar');
const service4 = container.get(MyService);
expect(service1).to.be.instanceof(MyService);
expect(service2).to.be.instanceof(MyService);
expect(service3).to.be.instanceof(MyService);
expect(service4).to.be.instanceof(MyService);
expect(service1).to.be.eq(service2);
expect(service2).to.be.not.eq(service3);
expect(service3).to.be.eq(service4);
expect(executed).to.be.eq(2);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
describe('class inheritance', function () {
it('should create an instance of the requested parent class even when its child class is registered', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ChildService);
const service = container.get(ParentService);
expect(service).to.be.instanceof(ParentService);
});
it('should create an instance of the requested child class even when its parent class is registered', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
const service = container.get(ChildService);
expect(service).to.be.instanceof(ChildService);
});
it('should return an instance of the requested child class instead of an existing instance of its parent class', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
const service1 = container.get(ParentService);
expect(service1).to.be.instanceof(ParentService);
const service2 = container.get(ChildService);
expect(service2).to.be.instanceof(ChildService);
expect(service2).to.be.not.eq(service1);
});
it('should prioritize the given registered constructor even its child class is registered too', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
container.add(ChildService);
const res1 = container.get(ParentService);
const res2 = container.get(ChildService);
expect(res1).to.be.instanceOf(ParentService);
expect(res2).to.be.instanceOf(ChildService);
expect(res1).to.be.not.eq(res2);
});
it('should prioritize the given registered constructor even its parent class is registered too', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
container.add(ChildService);
const res1 = container.get(ChildService);
const res2 = container.get(ParentService);
expect(res1).to.be.instanceOf(ChildService);
expect(res2).to.be.instanceOf(ParentService);
expect(res1).to.be.not.eq(res2);
});
});
describe('in case of a parent container', function () {
it('should instantiate in the parent container', function () {
class MyService {}
const parent = new ServiceContainer();
parent.add(MyService);
const child = new ServiceContainer(parent);
const res = child.get(MyService);
expect(res).to.be.instanceof(MyService);
});
it('should return an instance from the parent container', function () {
class MyService {}
const parent = new ServiceContainer();
const service = parent.get(MyService);
const child = new ServiceContainer(parent);
const res = child.get(MyService);
expect(res).to.be.eq(service);
});
it('should not add the given constructor to the parent container', function () {
class MyService {}
const parent = new ServiceContainer();
const child = new ServiceContainer(parent);
const service = child.get(MyService);
expect(service).to.be.instanceof(MyService);
const res1 = child.has(MyService);
const res2 = parent.has(MyService);
expect(res1).to.be.true;
expect(res2).to.be.false;
});
});
});
});
describe('getRegistered', function () {
it('should throw Error if the given constructor is not registered', function () {
const container = new ServiceContainer();
class MyService extends Service {}
const throwable = () => container.getRegistered(MyService);
expect(throwable).to.throw('Constructor MyService is not registered.');
});
it('should pass arguments to the "get" method and return a result', function () {
const container = new ServiceContainer();
class MyService extends Service {}
const spy = createSpy(container, 'get', () => 'result');
container.add(MyService);
const res = container.getRegistered(MyService, 'arg1', 'arg2');
expect(spy.callCount).to.be.eq(1);
expect(spy.calls[0].args).to.be.eql([MyService, 'arg1', 'arg2']);
expect(res).to.be.eql('result');
});
});
describe('has', function () {
describe('Service', function () {
it('should return true when a given constructor has its cached instance or false', function () {
const container = new ServiceContainer();
class MyService extends Service {}
expect(container.has(MyService)).to.be.false;
container.get(MyService);
expect(container.has(MyService)).to.be.true;
});
it('should return true when a given constructor has its factory function or false', function () {
const container = new ServiceContainer();
class MyService extends Service {}
expect(container.has(MyService)).to.be.false;
container.add(MyService);
expect(container.has(MyService)).to.be.true;
});
describe('class inheritance', function () {
it('should return false for the parent class even when its child class is registered', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ChildService);
const res = container.has(ParentService);
expect(res).to.be.false;
});
it('should return false for the child class even when its parent class is registered', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
const res = container.has(ChildService);
expect(res).to.be.false;
});
});
describe('in case of a parent container', function () {
it('should return true if the parent container has the given constructor', function () {
class MyService extends Service {}
const parent = new ServiceContainer();
parent.add(MyService);
const child = new ServiceContainer(parent);
const res = child.has(MyService);
expect(res).to.be.true;
});
});
});
describe('non-Service', function () {
it('should return true when a given constructor has its cached instance or false', function () {
const container = new ServiceContainer();
class MyService {}
expect(container.has(MyService)).to.be.false;
container.get(MyService);
expect(container.has(MyService)).to.be.true;
});
it('should return true when a given constructor has its factory function or false', function () {
const container = new ServiceContainer();
class MyService {}
expect(container.has(MyService)).to.be.false;
container.add(MyService);
expect(container.has(MyService)).to.be.true;
});
describe('class inheritance', function () {
it('should return false for the parent class even when its child class is registered', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ChildService);
const res = container.has(ParentService);
expect(res).to.be.false;
});
it('should return false for the child class even when its parent class is registered', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
const res = container.has(ChildService);
expect(res).to.be.false;
});
});
describe('in case of a parent container', function () {
it('should return true if the parent container has the given constructor', function () {
class MyService {}
const parent = new ServiceContainer();
parent.add(MyService);
const child = new ServiceContainer(parent);
const res = child.has(MyService);
expect(res).to.be.true;
});
});
});
});
describe('add', function () {
it('should require the parameter "ctor" to be a Function', function () {
const container = new ServiceContainer();
const throwable = v => () => container.add(v);
const error = v =>
format(
'Parameter "ctor" must be a class constructor, but %s given.',
v,
);
expect(throwable()).to.throw(error('undefined'));
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable(null)).to.throw(error('null'));
expect(throwable([])).to.throw(error('Array'));
expect(throwable({})).to.throw(error('Object'));
});
describe('Service', function () {
it('should return itself', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const res = container.add(MyService);
expect(res).to.be.eq(container);
});
it('should provide given arguments to the factory function', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('should override a cached instance of the given constructor', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const service1 = container.get(MyService);
const service2 = container.get(MyService);
expect(service1).to.be.eq(service2);
container.add(MyService);
const service3 = container.get(MyService);
const service4 = container.get(MyService);
expect(service3).to.be.eq(service4);
expect(service3).to.be.not.eq(service1);
});
it('should override constructor arguments of the factory function', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
container.add(MyService, 'baz', 'qux');
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
});
describe('non-Service', function () {
it('should return itself', function () {
class MyService {}
const container = new ServiceContainer();
const res = container.add(MyService);
expect(res).to.be.eq(container);
});
it('should provide given arguments to the factory function', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('should override a cached instance of the given constructor', function () {
class MyService {}
const container = new ServiceContainer();
const service1 = container.get(MyService);
const service2 = container.get(MyService);
expect(service1).to.be.eq(service2);
container.add(MyService);
const service3 = container.get(MyService);
const service4 = container.get(MyService);
expect(service3).to.be.eq(service4);
expect(service3).to.be.not.eq(service1);
});
it('should override constructor arguments of the factory function', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
container.add(MyService, 'baz', 'qux');
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
});
});
describe('use', function () {
it('should require the parameter "ctor" to be a Function', function () {
const container = new ServiceContainer();
const throwable = v => () => container.use(v);
const error = v =>
format(
'Parameter "ctor" must be a class constructor, but %s given.',
v,
);
expect(throwable()).to.throw(error('undefined'));
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable(null)).to.throw(error('null'));
expect(throwable([])).to.throw(error('Array'));
expect(throwable({})).to.throw(error('Object'));
});
describe('Service', function () {
it('should return itself', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const res = container.use(MyService);
expect(res).to.be.eq(container);
});
it('should pass itself and given arguments to the given constructor', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.use(MyService, 'foo', 'bar');
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('should override an existing factory function', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
container.use(MyService, 'baz', 'qux');
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
it('should cache a new instance', function () {
let executed = 0;
let service;
class MyService extends Service {
constructor(container) {
super(container);
++executed;
service = this;
}
}
const container = new ServiceContainer();
container.use(MyService);
const cachedService = container.get(MyService);
expect(cachedService).to.be.instanceof(MyService);
expect(cachedService).to.be.eq(service);
expect(executed).to.be.eq(1);
});
it('should override the cached instance', function () {
let executed = 0;
let service;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
++executed;
service = this;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.use(MyService, 'foo');
expect(executed).to.be.eq(1);
expect(service).to.be.instanceof(MyService);
expect(givenArgs).to.be.eql(['foo']);
const service1 = service;
container.use(MyService, 'bar');
expect(executed).to.be.eq(2);
expect(service).to.be.instanceof(MyService);
expect(givenArgs).to.be.eql(['bar']);
const service2 = service;
expect(service2).to.be.not.eq(service1);
});
});
describe('non-Service', function () {
it('should return itself', function () {
class MyService {}
const container = new ServiceContainer();
const res = container.use(MyService);
expect(res).to.be.eq(container);
});
it('should pass given arguments to the given constructor', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.use(MyService, 'foo', 'bar');
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('should override an existing factory function', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
container.use(MyService, 'baz', 'qux');
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
it('should cache a new instance', function () {
let executed = 0;
let service;
class MyService {
constructor() {
++executed;
service = this;
}
}
const container = new ServiceContainer();
container.use(MyService);
const cachedService = container.get(MyService);
expect(cachedService).to.be.instanceof(MyService);
expect(cachedService).to.be.eq(service);
expect(executed).to.be.eq(1);
});
it('should override the cached instance', function () {
let executed = 0;
let service;
let givenArgs;
class MyService {
constructor(...args) {
++executed;
service = this;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.use(MyService, 'foo');
expect(executed).to.be.eq(1);
expect(service).to.be.instanceof(MyService);
expect(givenArgs).to.be.eql(['foo']);
const service1 = service;
container.use(MyService, 'bar');
expect(executed).to.be.eq(2);
expect(service).to.be.instanceof(MyService);
expect(givenArgs).to.be.eql(['bar']);
const service2 = service;
expect(service2).to.be.not.eq(service1);
});
});
});
describe('set', function () {
it('should require the parameter "ctor" to be a Function', function () {
const container = new ServiceContainer();
const throwable = v => () => container.set(v, {});
const error = v =>
format(
'Parameter "ctor" must be a class constructor, but %s given.',
v,
);
expect(throwable()).to.throw(error('undefined'));
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable(null)).to.throw(error('null'));
expect(throwable([])).to.throw(error('Array'));
expect(throwable({})).to.throw(error('Object'));
throwable(String)();
});
it('should require the parameter "service" to be an Object', function () {
const container = new ServiceContainer();
const throwable = v => () => container.set(String, v);
const error = v =>
format('Parameter "service" must be an Object, but %s given.', v);
expect(throwable()).to.throw(error('undefined'));
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable(null)).to.throw(error('null'));
expect(throwable([])).to.throw(error('Array'));
throwable({})();
});
describe('Service', function () {
it('should return itself', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const res = container.set(MyService, {});
expect(res).to.be.eq(container);
});
it('should set the given service', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const service = {};
container.set(MyService, service);
const res = container.get(MyService);
expect(res).to.be.eq(service);
});
it('should override by the given service', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const service1 = {foo: 'bar'};
const service2 = {bar: 'baz'};
container.set(MyService, service1);
container.set(MyService, service2);
const res = container.get(MyService);
expect(res).to.be.eq(service2);
});
});
describe('non-Service', function () {
it('should return itself', function () {
class MyService {}
const container = new ServiceContainer();
const res = container.set(MyService, {});
expect(res).to.be.eq(container);
});
it('should set the given service', function () {
class MyService {}
const container = new ServiceContainer();
const service = {};
container.set(MyService, service);
const res = container.get(MyService);
expect(res).to.be.eq(service);
});
it('should override by the given service', function () {
class MyService {}
const container = new ServiceContainer();
const service1 = {foo: 'bar'};
const service2 = {bar: 'baz'};
container.set(MyService, service1);
container.set(MyService, service2);
const res = container.get(MyService);
expect(res).to.be.eq(service2);
});
});
});
});