我正在尝试为我的班级创建单元测试:
MyService.js:
const ApiServce = require('./api-service')
const Config = require('./config')
const Redis = require('ioredis')
class MyService {
constructor () {
const self = this
self.apiService = new ApiServce('MyService', '1.0.0', Config.port)
self.registerRoutes() //this invokes self.apiSerivce.registerRoutes
self.redis = new Redis(Config.redisport, Config.redishost)
self.queueKey = Config.redisqueuekey
}
run () {
const self = this
self.apiService.run()
}
}
module.exports = MyService
Config.js
module.exports = {
port: process.env.SVC_PORT || 8070,
redishost: process.env.REDIS_HOST || '127.0.0.1',
redisport: process.env.REDIS_PORT || 6379,
redisqueuekey: process.env.REDIS_Q_KEY || 'myeventqueue'
}
测试文件:
const Redis = require('ioredis')
const MyService = require('../src/myservice')
const ApiService = require('../src/api-service')
const Chai = require('chai')
const Sinon = require('sinon')
const SinonChai = require('sinon-chai')
Chai.use(SinonChai)
const should = Chai.should()
const expect = Chai.expect
describe('MyService', function () {
let apiservicestub, redisstub, apiconststub
beforeEach(function () {
apiservicestub = Sinon.stub(ApiService.prototype, 'registerRoutes')
redisstub = Sinon.stub(Redis.prototype, 'connect')
redisstub.returns(Promise.resolve())
})
describe('.constructor', function () {
it('creates instances of api service and redis client with correct parameters', Sinon.test(function () {
try {
const service = new MyService()
expect(apiservicestub).called
expect(redisstub).called
} catch (e) {
console.error(e)
expect(false)
}
}))
问题,问题:
Sinon有没有办法实现这个目标?我是否需要重新构建代码以满足Sinon的要求?
我也为1和2尝试了createStubInstance,但仍然遇到错误。
任何建议都将受到赞赏。
答案 0 :(得分:1)
为了使CommonJS模块可以在没有额外措施的情况下进行测试,类应该在整个应用程序中专门用作exports
对象的属性。这些类应该从模块对象就地进行解析。这不太方便,但它只适用于Sinon。
即
class ApiService {...}
exports.ApiService = ApiService;
...
const apiServiceModule = require('./api-service');
class MyService {
constructor () {
const { ApiService } = apiServiceModule;
...
在这种情况下,可以在MyService
实例化之前模拟模块对象的属性。 Sinon间谍不能正确支持类,构造函数应该被包装:
sinon.stub(apiServiceModule, 'ApiService', function MockedApiService(...) {
return new class { constructor (...) ... };
})
或者,可以使用DI,并且应该根据该重构应用程序。现有的DI库(injection-js
,inversify
,pioc
)可以合理地处理这个工作,但是一个简单的DI模式看起来像这样:
class MyService {
constructor (ApiService, ...) {
...
在这种情况下,可以在构造中提供所有依赖项 - 包括应用程序和测试。
但最简单的方法是使用面向测试的软件包,这些软件包会混淆模块缓存并允许控制require
个调用(rewire
,proxyquire
,mock-require
)
答案 1 :(得分:0)
更新了测试文件,感谢@estus的指示:
const Redis = require('ioredis')
const ApiService = require('../src/api-service')
const Chai = require('chai')
const Sinon = require('sinon')
const SinonChai = require('sinon-chai')
const Proxyquire = require('proxyquire')
const MyService = require('../src/myservice')
Chai.use(SinonChai)
const should = Chai.should()
const expect = Chai.expect
var namespace = {
apiServiceStubClass: function () {
},
redisStubClass: function () {
}
}
describe('MyService', function () {
let ProxiedMyService
let apiservicestub, redisstub, regroutestub, configstub, apiserviceregroutes, ioredisstub
beforeEach(function () {
apiservicestub = Sinon.stub(namespace, 'apiServiceStubClass')
redisstub = Sinon.stub(namespace, 'redisStubClass')
configstub = {
version: 'testversion',
port: 9999,
redishost: 'testhost',
redisport: 9999,
redisrteventqueuekey: 'testqueyekey'
}
ProxiedMyService = Proxyquire('../src/myservice', {
'./api-service': apiservicestub,
'./config': configstub,
'ioredis': redisstub
})
regroutestub = Sinon.stub(ProxiedMyService.prototype, 'registerRoutes')
regroutestub.returns(true)
apiserviceregroutes = Sinon.stub(ApiService.prototype, 'registerRoutes')
regroutestub.returns(true)
ioredisstub = Sinon.stub(Redis.prototype, 'connect')
ioredisstub.returns(Promise.resolve())
})
afterEach(function () {
namespace.apiServiceStubClass.restore()
namespace.redisStubClass.restore()
ProxiedMyService.prototype.registerRoutes.restore()
ApiService.prototype.registerRoutes.restore()
Redis.prototype.connect.restore()
})
describe('.constructor', function () {
it('creates instances of api service and redis client with correct parameters', Sinon.test(function () {
const service = new ProxiedMyService()
expect(apiservicestub).to.have.been.calledWithNew
expect(apiservicestub).to.have.been.calledWith('MyService', 'testversion', 9999)
expect(regroutestub).to.have.been.called
expect(redisstub).to.have.been.calledWithNew
expect(redisstub).to.have.been.calledWith(9999, 'testhost')
expect(service.queueKey).to.be.equal('testqueyekey')
}))
it('creates redis client using host only when port is -1', Sinon.test(function () {
configstub.redisport = -1
const service = new ProxiedMyService()
expect(redisstub).to.have.been.calledWith('testhost')
}))
})
describe('.registerRoutes', function () {
it('calls apiService registerRoutes with correct url and handler', Sinon.test(function () {
const service = new MyService()
expect.....
}))
})