TDD 系统被测创建模式(AutoFixture)

标签 tdd moq xunit autofixture

我正在尝试使用 SUT Factory “模式”来创建我的 SUT。

给定 SUT 结构:

namespace MySut
{
    public class Dep1
    {
    }

    public class Dep2
    {
    }

    public class Sut
    {
        public Sut( Dep1 dep1, Dep2 dep2 )
        {
        }
    }
}

我正在使用 AutoFixture ,并且想知道什么是折叠以下规范和关联的 SUT 工厂方法的最佳方法[有值(value)但]繁忙:

namespace MySpecifications
{
    using MySut;
    public class MySpecification
    {
        public void TestCore()
        {
            // Dont care about dependencies, testing core functionality
            var sut = CreateSut();
        }

        public void TestDep1Interaction()
        {
            // Dont care about Dep2, want to observe stuff on the Dep1 dependent object
            var sut = CreateSut( new Mock<Dep1>().Object );
        }

        public void TestDep2Interaction()
        {
            // Dont care about Dep1, want to observe stuff on the Dep2 dependent object
            var sut = CreateSut( new Mock<Dep2>().Object );
        }

        private object CreateSut( )
        {
            return CreateSut( CreateDep1(), CreateDep2() );
        }

        private object CreateSut( Dep1 dep1 )
        {
            return CreateSut( dep1, CreateDep2() );
        }

        private object CreateSut( Dep2 dep2 )
        {
            return CreateSut( CreateDep1(), dep2 );
        }

        private Sut CreateSut( Dep1 dep1, Dep2 dep2 )
        {
            return new Sut( dep1, dep2 );
        }

        private static Dep1 CreateDep1()
        {
            return new Fixture().CreateAnonymous<Dep1>();
        }

        private static Dep2 CreateDep2()
        {
            return new Fixture().CreateAnonymous<Dep2>();
        }
    }
}

类似于:

    public class MyAutoFixturedSpecification
    {
        public void TestCore()
        {
            // Dont care about dependencies, testing core functionality
            var sut = CreateSut();
        }

        public void TestDep1Interaction()
        {
            // Dont care about Dep2, want to observe stuff on the Dep1 dependent object
            var sut = CreateSut( new Mock<Dep1>().Object );
        }

        public void TestDep2Interaction()
        {
            // Dont care about Dep1, want to observe stuff on the Dep2 dependent object
            var sut = CreateSut( new Mock<Dep2>().Object );
        }

        private object CreateSut( params object[] injectedNonAnonymouses )
        {
            return new Fixture(  ).Build<Sut>(  )./*??????*/;
        }
    }

或:

    public class MyAnticipatedAutoFixturedSpecification
    {
        public void TestCore()
        {
            // Dont care about dependencies, testing core functionality
            var sut = new Fixture(  ).Build<Sut>().CreateAnonymous(  );
        }

        public void TestDep1Interaction()
        {
            // Dont care about Dep2, want to observe stuff on the Dep1 dependent object
            var sut = new Fixture().Build<Sut>()/*.With( new Mock<Dep1>().Object )*/.CreateAnonymous();
        }

        public void TestDep2Interaction()
        {
            // Dont care about Dep1, want to observe stuff on the Dep2 dependent object
            var sut = new Fixture().Build<Sut>()/*.With( new Mock<Dep2>().Object )*/.CreateAnonymous();
        }
    }

即移除所有工厂垃圾,以便我的规范可以轻松应对过渡到:

namespace MySutWithNewDependency
{
    public class Dep1
    {
    }

    public class Dep2
    {
    }

    public class Dep3
    {
    }

    public class Sut
    {
        public Sut( Dep1 dep1, Dep2 dep2, Dep3 dep3 )
        {
        }
    }
}

虽然与自动模拟容器的概念有重叠,但我仍然不是在寻找金锤 - 只是一种能够一次模拟 0 或 1 事物并且能够自定义创建依赖项的方法对象而无需在其依赖项集发生变化时重新访问对 Sut 构造函数的显式调用。

(还使用 xUnit.net(SubSpec 样式)、Moq、Ninject2(尽管不想在我的规范中使用 DI))

最佳答案

AutoFixture 可以或多或少地为您完成所有这些工作,而无需所有额外的脚手架。我将从一个基本的 Fixture 开始,并用它来解析 Sut:

var fixture = new Fixture();
var sut = fixture.CreateAnonymous<Sut>();

这假定 Dep1Dep2 可以由 AutoFixture 自动创建,但如前所述,它们可以,因为它们具有默认构造函数。

当你想覆盖一个特定的类型时,你可以像这样使用Register方法:

var fixture = new Fixture();

var mock = new Mock<Dep1>();
fixture.Register(mock.Object);
// Setup mock if necessary...

var sut = fixture.CreateAnonymous<Sut>();

这将导致 fixture 在需要 Dep1 时使用 mock.Object,包括 Sut构造函数被调用。这绝对符合您在面对不断发展的构造函数时保持稳健的要求,这也是 AutoFixture 如此构建的主要原因之一。

在更现实的场景中,Dep1 和 Dep2 可能是接口(interface),在这种情况下,您可能希望将它们注册为“默认夹具”的一部分。这当然也可能是因为最后调用 Register 获胜。这意味着您可以使用一些好的默认值配置 Fixture 实例,并且仍然能够在需要时覆盖特定类型。

我个人使用 AutoFixture 作为自动模拟容器。 This discussion提供了有关如何使用 AutoFixture 1.1 API 实现这一点的提示,但 AutoFixture 2.0 的新内核将提供更好的可扩展性选项。当我开始时,我会写一篇关于这个主题的博文。

附言很抱歉回复晚了,但我之前没有看到你的问题。以后,请随时 ping 我(例如在 Twitter 上)以获得更快的回复。

关于TDD 系统被测创建模式(AutoFixture),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3158148/

相关文章:

javascript - 回调 spy 不会在流结束时被调用

Java测试驱动开发

asp.net-mvc - ASP.NET MVC - 单元测试过度? (时分驱动)

c# - 什么时候应该使用 Moq 的 .As 方法?

c# - xUnit : Mutiple Assertions or Soft Assert

c# - 对接口(interface)存储库进行单元测试的目的是什么

c# - 如何使用 Moq 使所有方法都可验证

c# - 最小起订量模拟和跟踪 session 值

c# - 无法使用 xunit 测试 mvc.controller 检查 returnType 是否为 HttpNotFoundResult

F# 单元测试和模式匹配断言