将对象构造与应用程序逻辑混合

时间:2014-06-22 09:54:19

标签: javascript unit-testing testing

我已经开始重构我的代码以使其可测试。我发现问题的一个方面是我将对象构造与应用程序逻辑混合在一起。如果我有一个名为SomeClass的构造函数,它执行应用程序逻辑但是实例化另一个类,当我尝试测试时,我会遇到问题:

function SomeClass() {

    //does lots of application type work
    //but then also instantiates a different object
    new AnotherClass();
}

测试变得困难,因为现在我需要找到一种在测试环境中创建AnotherClass的方法。

我使用依赖注入处理了这个问题。因此SomeClassAnotherClass的实例作为参数:

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仍然依赖于工厂。我该如何正确解决这个问题?

1 个答案:

答案 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.jssomeclass.js,并在测试someclass.js时使用someclass-test.jsSomeClass

当然,这是一个粗略的草图;我假设您的真实世界应用可能并不具备全局(SomeClassAnotherClass),但是它包含{{1} }}和SomeClass可能也可用于包含AnotherClass,并包含对它及其各种假SomeClass的测试。