6

我已经看到了多个关于“如何存根您的课程,以便您可以控制 SUT 中发生的事情”的答案。

他们说一件事:

创建一个接口并使用依赖注入注入该接口,并使用您随后注入 SUT 的相同接口创建一个存根。

但是,我在以前的工作场所学到的东西:

如果您进行单元测试,您将测试所有类/功能。

这是否意味着对于每个具有特定功能布局的类,您必须创建一个接口?

这意味着类/文件的数量大约是原来的两倍。

如下例所示,这是“要走的路”还是我在单元测试过程中遗漏了什么?

注意:我使用的是 VS2012 Express。这意味着没有“Faker”框架。我正在使用“标准”VS2012 单元测试框架。

作为一个非常非常简单的示例,它允许我对传递给 SUT 的每个接口进行存根。

IFoo.cs

public interface IFoo
{
    string GetName();
}

Foo.cs

public class Foo : IFoo
{
    public string GetName()
    {
        return "logic goes here";
    }
}

IBar.cs:

public interface IBar : IFoo
{
    IFoo GetFoo();
}

酒吧.cs:

public class Bar : IBar
{
    public string GetName()
    {
        return "logic goes here";
    }

    public IFoo GetFoo()
    {
        return null; // some instance of IFoo
    }
}

IBaz.cs:

public interface IBaz
{
    IBar GetBar();
}

Baz.cs:

public class Baz
{
    public IBar GetBar()
    {
        return null; // some instance of IBar
    }
}
4

5 回答 5

6

在我看来,你不应该仅仅为了单元测试的目的而创建接口。如果您开始添加代码抽象来取悦工具,那么它们并不能帮助您提高工作效率。理想情况下,您编写的代码应该服务于特定的业务目的/需求——直接或间接地通过使代码库更易于维护或发展来实现。

接口有时会这样做,但肯定并非总是如此。我发现为组件提供接口通常是一件好事,但尽量避免为内部类使用接口(即代码仅在给定项目内部使用,无论类型是否声明为公共)。这是因为一个组件(例如,一组一起工作以解决某些特定问题的类)代表一个更大的概念(例如记录器或调度程序),这是我在测试时可能想要替换或存根的东西.

解决方案(向 Robert 致敬,因为他是评论中的第一位)是使用模拟框架在运行时生成兼容的替换类型。然后,模拟框架允许您验证被测试的类是否与替换的虚拟对象正确交互。如前所述,起订量是一个时髦的选择。Rhino.Mocks 和 NMock 是另外两个流行的框架。Typemock Isolator 与分析器挂钩,是更强大的选项之一(甚至允许您替换非虚拟私有成员),但它是一种商业工具。

为你应该进行多少单元测试制定规则是没有好处的。这取决于您正在开发什么以及您的目标是什么 - 如果正确性总是胜过上市时间并且成本不是一个因素,那么单元测试一切都很好。大多数人没有那么幸运,他们必须做出妥协才能达到合理的测试覆盖率。你应该测试多少也可能取决于团队的整体技能水平、预期的生命周期和正在编写的代码的重用等。

于 2013-05-24T00:14:32.270 回答
4

是和不是。为了存根依赖,您需要某种抽象,但这主要是因为模拟框架的工作方式(自然不是全部)。

考虑简单的例子。您测试A将依赖项与类BC. 为了使单元测试A工作,您需要模拟BC- 您需要IBIC或基类 /w 虚拟成员)。你需要IA吗?不,至少对于这个测试来说不是。除非A成为对其他类的依赖,否则不需要在接口/基类后面抽象它。

抽象很棒,因为它可以帮助您构建松散耦合的代码。您应该抽象您的依赖项。然而,在实践中,一些类不需要抽象,因为它们服务于顶层/层次结构末端/根角色,并且不会在其他地方使用。

于 2013-05-24T08:56:08.703 回答
3

也许从纯粹主义者的角度来看,这是正确的方法,但真正重要的是确保外部依赖项(例如数据库、网络访问等)、任何计算成本高/耗时的东西,以及任何不是完全确定性在单元测试中被抽象出来并且易于替换。

于 2013-05-23T23:45:11.057 回答
0

从测试的角度来看,没有必要为代码中的每个类都创建一个接口。您创建一个接口来隐藏抽象层后面的外部依赖项的具体执行。因此,您无需将需要直接 HTTP 连接的类与您的逻辑混合,而是将连接代码隔离到一个类,让它实现一个作为您的类成员的接口,并在该接口中注入一个模拟. 这样,您可以独立地测试您的逻辑,没有依赖关系,并且唯一“未经测试”的代码是可以通过其他方式测试的样板 HTTP 连接代码。

于 2013-05-24T01:13:16.947 回答
0

我会走虚拟方法路线。为您需要测试的每个类创建接口真的很麻烦,尤其是当您每次想要查看方法的定义时都需要像 Resharper 这样的工具来“转到实现”时。每当更改方法签名或添加新属性或方法时,都会产生管理和修改这两个文件的开销。

于 2015-06-23T22:39:58.357 回答