遗留代码的单元测试,对象创建

时间:2015-07-16 00:18:55

标签: unit-testing mocking

我正在将单元测试添加到现有应用程序中。阶级关系是这样的:

class TopClass creates class A and B and common class AB;
class A creates class A1, A2, A3, A4 and AB;
class B creates class B1 and AB;

我被告知在创建顶级对象时传递所有依赖对象(作为接口)以进行依赖注入。所以类构造函数需要更改为:

TopClass(A a, A1 a1, A2 a2, A3 a3, A4 a4, AB ab, B b, B1 b1)
A(A1 a1, A2 a2, A3 a3, A4 a4, AB ab)
B(B1 b1, AB ab)

这是正确/好的方式吗?有没有办法在应用程序开头不需要创建所有对象?

3 个答案:

答案 0 :(得分:2)

不,这不是一个好方法。

如果可能,您只需要传入界面 A和界面 B

TopClass(A a, B b)

这种方式对TopClass的单元测试只担心它的直接依赖性而不是依赖性的依赖性。这也可以让您在测试中模拟AB的实现。

然后,对于A的测试,您可能有一个传递A1,A2等的构造函数,但TopClass不应该知道。

答案 1 :(得分:1)

简短回答:是的,那是你开始的地方,但不一定是一个好的结局。 TopClass应该只关心ABAB的(接口)。这些对象的实现和依赖性与TopClass无关。这也意味着模拟TopClass应该非常简单:你需要一个模拟A,一个模拟B和一个模拟AB,你可以直接传递它们。

如何在不关心A依赖关系的情况下创建A?而不是A实例,接收Provider<A>,其中Provider是Java的内置无参数工厂接口。

此时,构造函数如下所示:

TopClass(A a, B b, AB ab)
A(A1 a1, A2 a2, A3 a3, A4 a4, AB ab)
B(B1 b1, AB ab)

如果您希望在需要之前延迟创建,或者如果您需要多个类型,则可以使用Provider<Type>替换这些类型中的任何类型。

&#34;但等等!&#34;我听到你哭了。 &#34;这是不是意味着我必须制作一系列复杂的样板供应商才能隐藏TopClass的实现/依赖细节?&#34; 是的,确实如此,您可以手动执行此操作 - 或者您可以使用依赖注入框架,如Spring,Guice或Dagger。

手册可能如下所示:

public class EntryPoint() {
  AB ab = new AB();

  Provider<TopClass> provideTopClass() {
    return new Provider<TopClass>() {
      @Override public TopClass get() {
        return new TopClass(provideA().get(), provideB().get(), provideAB().get());
      }
    };
  }

  Provider<A> provideA() {
    return new Provider<AB>() {
      @Override public AB get() {
        return new A(provideA1().get(), provideA2.get(), ...);
      }
    };
  }

  Provider<AB> provideAB() {
    return new Provider<AB>() {
      @Override public AB get() {
        return ab;   // always reuse the same instance if you'd like
      }
    };
  }

  // and so forth
}

在那里,您刚刚将所有创建和连接提取到一个类中,Guice可以在运行时模拟,Dagger可以在编译时直接生成。您需要与您的团队一起选择您想要的框架,或者手动启动,但总体而言,这应该为制作易于组装且易于测试的应用程序提供很多奖励。

答案 2 :(得分:0)

如果按此顺序创建实例:

AB ab = new AB();//and so on for all classes wihtout dependencies like a1, a2, ..., b1
A a = new A(a1, a2, a3, a4, ab)
B b = new B(b1, ab)
TopClass(a, b, ab)

但正如你所说,TopClass创建了这些类的实例,这就是实际问题。它应该期望实例,而不是创建它们。 也许这样它甚至不需要ab参数,如果它只用它来创建A和B.

使用一些依赖注入框架/库可以是一种从开发人员那里拿走大量样板的方法,但其成本如

  • efford更改代码以使其在应用程序和测试中工作
  • 运行时性能(取决于框架)

应该考虑。