C#存根。每个可测试对象的接口?

C#存根。每个可测试对象的接口?,c#,unit-testing,vs-unit-testing-framework,C#,Unit Testing,Vs Unit Testing Framework,关于“如何存根类以便控制SUT中发生的事情”,我已经看到了多个答案 他们说了一件事: 创建一个接口并使用依赖项注入注入该接口,并使用随后注入到SUT中的同一接口创建存根 然而,我在以前的工作场所学到的是: 如果进行单元测试,则测试所有类/功能 这是否意味着对于每个具有特定函数布局的类,都必须创建一个接口 这意味着类/文件的数量将是原来的两倍 如下面的例子所示,这是“要走的路”还是我在单元测试过程中遗漏了什么 请注意: 我正在使用VS2012 Express。这意味着没有“伪造”的框架。我使用的是

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

他们说了一件事:

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

然而,我在以前的工作场所学到的是:

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

这是否意味着对于每个具有特定函数布局的类,都必须创建一个接口

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

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

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

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

IFoo.cs

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

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

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

    public IFoo GetFoo()
    {
        return null; // some instance of IFoo
    }
}
public interface IBaz
{
    IBar GetBar();
}
public class Baz
{
    public IBar GetBar()
    {
        return null; // some instance of IBar
    }
}
Bar.cs:

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

    public IFoo GetFoo()
    {
        return null; // some instance of IFoo
    }
}
public interface IBaz
{
    IBar GetBar();
}
public class Baz
{
    public IBar GetBar()
    {
        return null; // some instance of IBar
    }
}
IBaz.cs:

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

    public IFoo GetFoo()
    {
        return null; // some instance of IFoo
    }
}
public interface IBaz
{
    IBar GetBar();
}
public class Baz
{
    public IBar GetBar()
    {
        return null; // some instance of IBar
    }
}
Baz.cs:

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

    public IFoo GetFoo()
    {
        return null; // some instance of IFoo
    }
}
public interface IBaz
{
    IBar GetBar();
}
public class Baz
{
    public IBar GetBar()
    {
        return null; // some instance of IBar
    }
}

也许从纯粹主义的角度来看,这是正确的做法,但真正重要的是确保外部依赖性(如数据库、网络访问等),任何计算上昂贵/耗时的东西,任何不完全确定的东西都会被抽象出来,并且很容易在单元测试中替换。

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

接口有时会这样做,但肯定不总是这样。我发现为组件提供接口通常是一件好事,但是尽量避免为内部类使用接口(也就是说,代码只在给定项目内部使用,而不管类型是否声明为公共)。这是因为一个组件(如中所示,一组协同工作以解决某些特定问题的类)代表一个更大的概念(如记录器或调度程序),这是我在测试时可能想要替换或删除的东西

解决方案(Robert在评论中名列第一)是使用模拟框架在运行时生成兼容的替换类型。然后,模拟框架允许您验证被测试的类是否与被替换的伪类正确交互。如前所述,最低起订量是一个时髦的选择。mock和NMock是另外两种流行的框架。Typemock隔离器与探查器挂钩,是更强大的选项之一(允许您替换非虚拟私有成员),但它是一种商业工具


为单元测试的数量制定规则是没有用的。这取决于您正在开发什么以及您的目标是什么——如果正确性总是胜过上市时间,并且成本不是一个因素,那么单元测试一切都很好。大多数人并没有这么幸运,他们将不得不妥协以实现合理水平的测试覆盖率。您应该测试多少还取决于团队的总体技能水平、预期寿命和正在编写的代码的重用等。

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

是和否。为了存根依赖关系,您需要某种抽象,但这主要是因为如何模拟框架(当然不是全部)

考虑一个简单的例子。您测试类
A
,该类获取类
B
C
的依赖项。对于
A
的单元测试,您需要模拟
B
C
——您需要
IB
IC
或基类/w虚拟成员)。您是否需要
IA
?不,至少这次测试没有。除非
A
依赖于其他类,否则不需要在接口/基类后面抽象它


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

我选择虚拟方法路线。为每个需要测试的类创建接口确实很麻烦,尤其是当您每次想查看方法的定义时都需要诸如Resharper之类的工具来实现“go to implementation”时。而且,每当更改方法签名或添加新的属性或方法时,都需要管理和修改这两个文件。

我的建议:找到一个好的模拟框架(Moq是一个不错的选择)这并不需要您创建所有这些接口。我会去看看framework@RobertHarvey:Moq将如何帮助避免创建接口?OP仍然需要为它留下入口点(Moq),无论是虚拟成员还是界面。@jimmy_keen:该死,我肯定Moq不需要这个。你可以肯定,我会找到一种方法来避免创建所有这些接口,只是为了让它们可以被模仿