2017-07-17 60 views
1

我有一個ES6類,它依賴於一些外部模塊的工作。由於這是一個節點應用程序,我使用CommonJS來要求和加載模塊。需要外部模塊作爲ES6類實例變量

然而,這種模塊加載使得單元測試變得複雜並不是什麼祕密。我當然可以通過構造函數依賴注入所有必需的模塊,但這在動態類型語言中感覺很麻煩。我也不喜歡使用像proxyquire這樣的庫,因爲它擴大了我的測試代碼。

所以我想出了將所需模塊存儲爲實例變量的想法。例如:

const someModule = require('some-module'); 

class MyClass { 

    constructor() { 
     this.someModule = someModule; 
    } 

    someFunction(value) { 
     return this.someModule.someFunction(value); 
    } 

} 

這樣我可以使用模塊加載裝載機的依賴,仍然窺視/存根/在我的單元測試嘲笑他們。

這被認爲是不好的做法,或者你能看到任何主要缺點嗎?

回答

1

這在個案的基礎上肯定是可以接受的。靜態或原型someModule屬性會更有效率,但另一方面,這將需要在測試中嘲笑後恢復它。

定期這種模式可能會變得麻煩,在這種情況下,DI容器可能會更方便。在Node領域有很多,例如injection-js that was extracted from Angular DI

在其最簡單的形式可以是不下隨機令牌創建自身實例,但現有的值(模塊出口)存儲一個純粹的單容器:

class Container extends Map { 
    get(key) { 
    if (this.has(key)) { 
     return super.get(key); 
    } else { 
     throw new Error('Unknown dependency token ' + String(key)); 
    } 
    } 

    set(key, val) { 
    if (key == null) { 
     throw new Error('Nully dependency token ' + String(key)); 
    } else if (arguments.length == 1) { 
     super.set(key, key); 
    } else { 
     super.set(key, val); 
    } 
    } 
} 

const container = new Container; 

的依賴可以登記與檢索直接從容器:

const foo = Symbol('foo'); 
container.set(foo, require('foo')); 
container.set('bar', require('bar')); 
container.set(require('baz')); 
... 
const { foo } = require('./common-deps'); 

class Qux { 
    constructor() { 
    this.foo = container.get(foo); 
    ... 
    } 
} 

此外,噴射器可以擁抱容器:

class DI { 
    constructor(container) { 
    this.container = container; 
    } 

    new(Fn) { 
    if (!Array.isArray(Fn.annotation)) { 
     throw new Error(Fn + ' is not annotated'); 
    } 

    return new Fn(...Fn.annotation.map(key => this.container.get(key))); 
    } 

    call(fn) { 
    if (!Array.isArray(fn.annotation)) { 
     throw new Error(fn + ' is not annotated'); 
    } 

    return fn(...fn.annotation.map(key => this.container.get(key))); 
    } 
} 

const di = new DI(container); 

照顧DI的註解類和函數(上標註,見this explanation):

class Qux { 
    constructor(foo, bar) { 
    this.foo = foo; 
    ... 
    } 
} 
Qux.annotation = [foo, 'bar', require('baz')]; 

quuxFactory.annotation = [require('baz')] 
function quuxFactory(baz) { ... } 

const qux = di.new(Qux); 
const quux = di.call(quuxFactory); 
+0

我會接受你的答案,並給予直接投資框架一試。非常感謝詳細的信息:)一些更多的信息:我更廣泛地瞭解了可用於節點的各種DI框架並嘗試了其中的幾個。我發現https://github.com/justmoon/constitute有一個非常乾淨和輕量級的API(似乎也受到了Angular DI概念的啓發)。 – benjiman

+0

不客氣。感謝分享,我會檢查它。作者承認它大部分複製了Aurelia DI,因此您可以查看[其手冊](http://aurelia.io/hub.html#/doc/article/aurelia/dependency-injection/latest/dependency-injection - 基礎/ 6)的一些概念。關於injection-js的好處是它支持並且在Angular手冊中有很好的文檔記錄,對於Angular的fullstack開發也非常簡單。 – estus