我已经开始重构我的代码以使其可测试。我发现问题的一个方面是我将对象构造与应用程序逻辑混合在一起。如果我有一个名为SomeClass
的构造函数,它执行应用程序逻辑但是实例化另一个类,当我尝试测试时,我会遇到问题:
function SomeClass() {
//does lots of application type work
//but then also instantiates a different object
new AnotherClass();
}
测试变得困难,因为现在我需要找到一种在测试环境中创建AnotherClass
的方法。
我使用依赖注入处理了这个问题。因此SomeClass
将AnotherClass
的实例作为参数:
function SomeClass(anotherObj) {
}
问题是,据我所知,所有这一切都是将问题推迟到我的应用程序的其他地方。我仍然需要在我的代码中的其他地方anotherObj
创建AnotherClass
。
这篇谷歌测试文章http://googletesting.blogspot.co.uk/2008/08/by-miko-hevery-so-you-decided-to.html建议:
为了拥有一个可测试的代码库,您的应用程序应该有两个 各种课程。这些工厂充满了新的"运营商 并负责构建应用程序的对象图, 但是没有做任何事情。应用程序逻辑类没有" new"操作员并负责工作。
这听起来和我的问题完全一样,工厂类型模式就是我需要的。所以我尝试了类似的东西:
function anotherClassFactory() {
return new AnotherClass();
}
function SomeClass() {
anotherClassFactory();
}
但是SomeClass
仍然依赖于工厂。我该如何正确解决这个问题?
答案 0 :(得分:1)
(我认为这是一个社区维基的答案,因为我坦率地认为它只回答了问题的一部分,而留下了太多未说明的内容。希望拥有更多知识的其他人可以改进它。)
但是
SomeClass
仍然依赖于工厂。我该如何正确解决这个问题?
根据您链接的this article链接,您可以这样做:
anotherclass.js
:
function AnotherClass() {
}
AnotherClass.prototype.foo = function() { /* ... */ };
AnotherClass.prototype.bar = function() { /* ... */ };
AnotherClass.prototype.baz = function() { /* ... */ };
someclass.js
:
function SomeClass(a) {
// ...app logic...
// Use AnotherClass instance `a`; let's say you're going to call `foo`,
// but have no use for `bar` or `baz`
a.foo();
// ...app logic...
}
someclass-test.js
:
function someClass_testSomething() {
var sc = new SomeClass({
foo: function() { /* ...appropriate `foo` code for this test... */}
});
// ...test `sc`...
}
function someClass_testSomethingElse() {
// ...
}
app.js
:
function buildApp() {
return {
// ...lots of various things, including:
sc: new SomeClass(new AnotherClass())
};
}
因此真正的应用程序是使用buildApp
构建的,它为SomeClass
实例提供了AnotherClass
。您对SomeClass
的测试会使用someClass_testSomething
等,它会使用真实的SomeClass
但是使用模拟的实例而不是真正的AnotherClass
就足够了{为了测试的目的,它就是它。
我的依赖注入力虽然很弱,但我坦率地看不出buildApp
如何扩展到现实世界,我也不知道你应该做什么,如果一个方法必须创建一个对象来完成它的工作,例如:
SomeClass.prototype.doSomething = function() {
// Here, I need an instance of AnotherClass; maybe I even need to
// create a variable number of them, depending on logic internal
// to the method.
};
你不会传递方法所需要的所有东西作为参数,这将是意大利面条的噩梦。这可能是为什么对于更多静态语言,通常有工具而不仅仅是编码模式。
在JavaScript中,当然,我们还有另一种选择:在代码中继续使用new AnotherClass
:
anotherclass.js
:
function AnotherClass() {
}
AnotherClass.prototype.foo = function() { /* ... */ };
AnotherClass.prototype.bar = function() { /* ... */ };
AnotherClass.prototype.baz = function() { /* ... */ };
someclass.js
:
function SomeClass() {
// ...app logic...
// Use AnotherClass instance `a`; let's say you're going to call `foo`,
// but have no use for `bar` or `baz`
(new AnotherClass()).foo();
// ...app logic...
}
someclass-test.js
:
var AnotherClass;
function someClass_testSomething() {
// Just enough AnotherClass for this specific test; there might be others
// for other tests
AnotherClass = function() {
};
AnotherClass.prototype.foo = function() { /* ...appropriate `foo` code for this test... */};
var sc = new SomeClass();
// ...test `sc`...
}
function someClass_testSomethingElse() {
// ...
}
您在真实应用中使用anotherclass.js
和someclass.js
,并在测试someclass.js
时使用someclass-test.js
和SomeClass
。
当然,这是一个粗略的草图;我假设您的真实世界应用可能并不具备全局(SomeClass
,AnotherClass
),但是它包含{{1} }}和SomeClass
可能也可用于包含AnotherClass
,并包含对它及其各种假SomeClass
的测试。