C#stubbing.每个可测试对象的接口?

Daa*_*mer 6 c# unit-testing vs-unit-testing-framework

我已经看到了关于'如何存根你的课程以便你可以控制SUT内发生的事情'的多个答案.

他们说一件事:

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

但是,我在以前的工作场所学到了什么:

如果进行单元测试,则测试所有类/功能.

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

这意味着类/文件的数量几乎是两倍.

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

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

作为一个非常非常简单的例子,它允许我将传递给SUT的每个接口存根.

IFoo.cs

public interface IFoo
{
    string GetName();
}
Run Code Online (Sandbox Code Playgroud)

Foo.cs

public class Foo : IFoo
{
    public string GetName()
    {
        return "logic goes here";
    }
}
Run Code Online (Sandbox Code Playgroud)

IBar.cs:

public interface IBar : IFoo
{
    IFoo GetFoo();
}
Run Code Online (Sandbox Code Playgroud)

Bar.cs:

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

    public IFoo GetFoo()
    {
        return null; // some instance of IFoo
    }
}
Run Code Online (Sandbox Code Playgroud)

IBaz.cs:

public interface IBaz
{
    IBar GetBar();
}
Run Code Online (Sandbox Code Playgroud)

Baz.cs:

public class Baz
{
    public IBar GetBar()
    {
        return null; // some instance of IBar
    }
}
Run Code Online (Sandbox Code Playgroud)

Mor*_*ner 6

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

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

解决方案(Robert在评论中首先提到)是使用模拟框架在运行时生成兼容的替换类型.然后,模拟框架允许您验证被测试的类是否与替换的虚拟对象正确交互.Moq是一个时髦的选择.Rhino.Mocks和NMock是另外两个流行的框架.Typemock Isolator挂钩到探查器中,是更强大的选项之一(允许你替换非虚拟私有成员),但它是一个商业工具.

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


k.m*_*k.m 4

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

考虑简单的例子。您测试A依赖于类B和 的类C。为了使单元测试正常A工作,您需要模拟Band C- 您需要IBand IC或基类 /w 虚拟成员)。你需要IA?不,至少对于这次测试来说不是。除非A成为对其他类的依赖,否则不需要将其抽象在接口/基类后面。

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