【问题标题】:Mixing object construction with application logic将对象构造与应用程序逻辑混合
【发布时间】:2014-06-22 09:54:18
【问题描述】:

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

function SomeClass() {

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

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

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

function SomeClass(anotherObj) {

}

据我所知,这个问题就是将问题推迟到我的应用程序中的其他地方。我仍然需要在我的代码中的其他地方从AnotherClass 创建anotherObj

这篇google测试文章http://googletesting.blogspot.co.uk/2008/08/by-miko-hevery-so-you-decided-to.html建议:

为了有一个可测试的代码库,您的应用程序应该有两个 类。工厂,这些都是“新”操作员 并负责构建应用程序的对象图, 但不要做任何事情。以及没有“新”运算符并负责工作的应用程序逻辑类。

这听起来和我的问题一模一样,而工厂类型模式正是我所需要的。所以我尝试了类似的方法:

function anotherClassFactory() {
    return new AnotherClass();
}

function SomeClass() {
    anotherClassFactory();
}

但是SomeClass 仍然依赖于工厂。我该如何正确解决这个问题?

【问题讨论】:

    标签: javascript unit-testing testing


    【解决方案1】:

    (我将其设为社区 wiki 答案,因为坦率地说,我认为它只回答了部分问题,而留下了太多未说的内容。希望其他有更多知识的人可以改进它。) p>

    但是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 如何扩展到现实世界,我也看不出如果 方法 em> 必须创建一个对象来完成它的工作,例如:

    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 时使用someclass.jssomeclass-test.js

    当然,这是一个粗略的草图;我假设您的实际应用程序可能到处都没有全局变量(SomeClassAnotherClass),但是您包含 SomeClassAnotherClass 大概也可以用于包含SomeClass,并包含对其的测试及其各种假AnotherClasss。

    【讨论】:

    • 感谢您的详细回答。我已经阅读了链接的文章,我认为您回答的第一部分解释了为什么我没有采用这种方法......在现实世界中,或者至少在现实世界中,当谈到 js 时,我是不会那样做。目前我得到了你在答案第二部分中建议的场景......这是模拟一个假的 AnotherClass。正如您所说,希望有人可以对此提出改进建议。
    猜你喜欢
    • 2015-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-07
    • 2011-04-10
    相关资源
    最近更新 更多