6

问题

我有一个相当大的应用程序,它利用了许多其他服务。对于测试场景,我不希望我的单元测试依赖于第三方系统,所以我想用假货或模拟等替换服务。

我已经完成了大部分繁重的工作,并将具体的服务替换为IService. 具体的服务与 DI 框架相连

现在我想用随机生成的假货替换那些。

编码

示例服务接口:

public interface ISimpleService
{
    int Fizz(int input);

    string Buzz(string input);
}

示例服务工厂接口:

public interface ISimpleServiceFactory
{
    ISimpleService Create();
}

实现尽可能简单

public static void Main(string[] args)
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    var service = fixture.Create<ISimpleService>();

    var fizzResult = service.Fizz(42);
    var buzzResult = service.Buzz("I'd like something random...");
}

这显示了我基本上想要的。我只想让 autofixture 为我创建一些动态代理对象,方法是返回接口中指定的随机类型的东西......

请注意,我在这里使用了 AutoMoq,因为默认情况下 Fixture 不想从接口创建对象,但我也尝试过其他框架:FakeItEasy、AutoRhinoMock

解决方法

通过手动设置所有内容来实现这种工作

public static void Main(string[] args)
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    var serviceMock = fixture.Create<Mock<ISimpleService>>();

    // These two lines below cause the magic now
   serviceMock.Setup(x => x.Fizz(It.IsAny<int>())).Returns(fixture.Create<int>());
   serviceMock.Setup(x => x.Buzz(It.IsAny<string>())).Returns(fixture.Create<string>());

    var service = serviceMock.Object;
    var fizzResult = service.Fizz(42);
    var buzzResult = service.Buzz("I'd like something random...");
}

这确实给了我想要的结果:带有随机 int 的 fizzResult,带有随机字符串的 BuzzResult(默认情况下为 guid)但是,这只是一个小例子,我的实际服务引用要大得多,最多有 100 种方法。 ..(它们是外部肥皂服务等,无能为力)我不想手动设置所有内容,如果有通用解决方案,那就太好了...

通过手动设置所有内容的工厂实现

因此,您可能已经注意到,我还发布了一个ISimpleServiceFactory界面。这和实际情况很像,因为实际的具体ISimpleService 需要一堆配置。所以,如果我们使用这种可行的解决方案,我们会得出这样的结论:

public static void Main(string[] args)
{
    var fixture = new Fixture().Customize(new AutoMoqCustomization());
    var serviceFactoryMock = fixture.Create<Mock<ISimpleServiceFactory>>();

    var serviceMockDelegate = new Func<ISimpleService>(() =>
        {
            var serviceMock = fixture.Create<Mock<ISimpleService>>();
            serviceMock.Setup(x => x.Fizz(It.IsAny<int>())).Returns(fixture.Create<int>());
            serviceMock.Setup(x => x.Buzz(It.IsAny<string>())).Returns(fixture.Create<string>());

            return serviceMock.Object;
        });

    serviceFactoryMock.Setup(x => x.Create()).Returns(serviceMockDelegate);
    var service = serviceFactoryMock.Object.Create();

    var fizzResult = service.Fizz(42);
    var buzzResult = service.Buzz("I'd like something random...");
}

这似乎有点乱,这是一个非常小的界面。实际的服务有很多层次,有上百种方法。

对于我的代码中需要特定条件的方法,我显然仍会手动设置这些条件,但默认情况下其他所有内容都应该是随机值。生成大量随机对象也允许进行一些模糊测试

有没有办法在不进行所有这些手动设置的情况下自动生成随机对象?

4

1 回答 1

3

您不需要工厂,也不需要在接口中设置每个方法,如果我理解正确,您只是想使用Fixture创建一个代理,该代理为您在该代理上调用的每个方法返回随机值。而不是使用AutoMoqCustomization使用AutoConfiguredMoqCustomization。这一切都在名为 Fixture.AutoMoq 的 nuget 包中。

class Program
{
    static void Main(string[] args)
    {
    }
}

[TestFixture]
public class program
{
    [Test]
    public void some_test()
    {
        var fixture = new Fixture();
        fixture.Customize(new AutoConfiguredMoqCustomization());

        var simpleServices = fixture.CreateMany<ISimpleService>();

        foreach (var simpleService in simpleServices)
        {
            string randomString = simpleService.Buzz("hello");
            int randomInt = simpleService.Fizz(15);
        }
    }
}

public interface ISimpleService
{
    int Fizz(int input);

    string Buzz(string input);
}
于 2015-09-07T04:47:10.927 回答