C# 是否可以重用代码进行集成和单元测试?

C# 是否可以重用代码进行集成和单元测试?,c#,unit-testing,language-agnostic,tdd,integration-testing,C#,Unit Testing,Language Agnostic,Tdd,Integration Testing,我使用的是一个具有单元和集成测试的分布式系统。我试图通过在集成测试和单元测试之间重用代码来节省时间和维护工作。为此,我实现了一个接口和两个类:fake和real。Fake类返回一些存根数据,而real类对其他分布式服务进行一些调用 我的项目的当前结构 /BaseTest interface IFoo ------------------------------------- /UnitTest class FakeFoo : IFoo [TestF

我使用的是一个具有单元和集成测试的分布式系统。我试图通过在集成测试和单元测试之间重用代码来节省时间和维护工作。为此,我实现了一个接口和两个类:fake和real。Fake类返回一些存根数据,而real类对其他分布式服务进行一些调用

我的项目的当前结构

/BaseTest interface IFoo ------------------------------------- /UnitTest class FakeFoo : IFoo [TestFixture] class FooTest {...} //uses FakeFoo ------------------------------------- /IntegrationTest class RealFoo : IFoo [TestFixture] class FooTest {...} //uses RealFoo /基本测试 接口IFoo ------------------------------------- /单元测试 类FakeFoo:IFoo [测试夹具] 类FooTest{…}//使用FakeFoo ------------------------------------- /集成测试 类RealFoo:IFoo [测试夹具] 类FooTest{…}//使用RealFoo 我想以某种方式为这两个测试重用代码,所以如果我有一个测试

[Test]
public void GetBarIsNotNullTest()
{
    var foo = IoC.Current.Resolve<IFoo>();
    Bar actual = foo.GetBar();
    Assert.IsNotNull(actual);   
}
[测试]
public void GetBarIsNotNullTest()
{
var foo=IoC.Current.Resolve();
Bar实际值=foo.GetBar();
Assert.IsNotNull(实际值);
}
我希望此测试同时运行两种实现:
RealFoo
FakeFoo
。到目前为止,我考虑过在/UnitTest和/IntegrationTest项目之间复制粘贴测试,但这听起来并不正确

系统是用C语言编写的,但我相信这个问题与语言无关


有人有更好的想法吗?我做错了吗?

您的测试场景出现了严重的问题

让我们先看看单元测试。您有一个提供可预测结果的依赖项存根。根据存根配置,您已经剪切了应该给出预期结果的部分。到目前为止还不错

但是。如果您想在集成测试中重用测试代码(断言),这实际上意味着您希望实际的依赖性实现产生与单元测试相同的结果。如果是这样的话,为什么不测试您的依赖性以给出这些结果并跳过整个代码层呢

更新

你的例子是错误的
FakeFoo
是一个存根,不应该进行测试。您可以使用存根来测试依赖于某些服务的类。因此,我们假设您正在测试某个依赖于
IFoo
的类
Bar
,这意味着:

[Test]
public void GetBarIsNotNullTest()
{
    var bar = IoC.Current.Resolve<Bar>();
    var actual = bar.GetDon();
    Assert.IsNotNull(actual);   
}
[测试]
public void GetBarIsNotNullTest()
{
var bar=IoC.Current.Resolve();
var实际值=bar.GetDon();
Assert.IsNotNull(实际值);
}
您在测试中使用了不同的
IFoo
实现

澄清我的立场

因为您在测试中复制了Act和Assert阶段,所以您很可能在CUT中测试相同的代码路径(
Bar
)。这意味着测试复制,并不比代码复制好多少


您应该通过使用伪代码(这将是单元测试)确保剪切(
)在所有代码路径上都是良好的。然后,您应该确保您的依赖项(
RealFoo
)返回预期的数据(这将是集成测试,因为它与分布式服务一起工作)。无需使用
RealFoo
测试
Bar
,因为它已经过全面测试。

编写自己的伪实现不会节省时间。创建依赖性模拟要容易得多:

Mock<IFoo> fooMock = new Mock<IFoo>();
这是不可能的假。您将有一个
Bar
属性的实现,它将返回
true
false


更新:单元测试和集成测试之间的唯一区别是测试的
安排
部分
Act
Assert
可以是相同的(如果您只进行状态测试,而不进行交互测试)。但是,
Arrange
是完全不同的。它不仅仅是创建直接依赖的实例。如果使用mock,则应该为SUT使用的成员设置返回结果。这将足以重现测试场景。但是对于真实的对象,您应该将堆栈中的所有依赖项设置为当前测试场景所需的某种状态。

即使其他对象的答案有好的地方,这也是我最终要做的。

我为单元和集成测试创建了一个基类

[TestFixture]
public class FooBase
{
    [Test]
    public void GetBarIsNotNullTest()
    {
        var foo = IoC.Current.Resolve<IFoo>();
        Bar actual = foo.GetBar();
        Assert.IsNotNull(actual);   
    }

    //many other tests  
}

因此,如果我现在运行我的测试,我会让父类
FooBase
中定义的测试在单元测试类和集成测试类中使用它们自己的真实和虚假对象运行两次。这是因为测试夹具的继承性。

为什么不将测试内容移动到共享库中,然后从单独的测试中调用它呢?我想你是对的——请参阅下面我对你答案的评论;我的问题是如何让它与常规MS测试框架(vs2012)一起工作?我不一定期望结果完全相同。例如,一个操作应该返回一个具有某些性质的对象,比如NOTNULL。在这种情况下我该怎么办?你建议只做集成测试,对吗?我可能不明白。我谈论的是断言,而不是结果。当你测试你的操作“某些品质”时,这是一个断言。当您有两个相同Act和Assert的测试时,您是在复制代码。我将在帖子中澄清我的观点。“你应该通过使用假货(这将是单元测试)确保你的剪切(条)在所有代码路径上都是好的。然后你应该确保你的依赖(RealFoo)返回预期的数据(这将是集成测试…)这种分层方法的问题是,您的集成测试可能通过(断言匹配服务数据),单元测试可能通过(断言匹配模拟数据),但服务数据可能与模拟数据不匹配。这两个测试层中的预期数据很容易不同步,因此,在整个过程中进行测试是有价值的,即使您正在重新测试一个零件。可以称之为集成测试、端到端测试或其他任何测试。将集成测试和单元测试分开的部分原因是集成测试需要很长时间。
[TestFixture]
public class FooBase
{
    [Test]
    public void GetBarIsNotNullTest()
    {
        var foo = IoC.Current.Resolve<IFoo>();
        Bar actual = foo.GetBar();
        Assert.IsNotNull(actual);   
    }

    //many other tests  
}
[TestFixture]
public class UnitTestFoo : FooBase
{
    [SetUp]
    public void SetUp()
    {
        IoC.Current.Register<IFoo, FakeFoo>();        
    }

    //nothing else here
}

[TestFixture]
public class IntegrationTestFoo : FooBase
{
    [SetUp]
    public void SetUp()
    {
        IoC.Current.Register<IFoo, RealFoo>();        
    }

    //nothing else here
}