如何使用NSubstitute和/或AutoFixture来测试具体类

Pra*_*hah 3 c# unit-testing autofixture nsubstitute

我希望通过使用AutoFixture和NSubstitue,我可以使用每个人提供的最好的东西.我自己使用NSubstitute取得了一些成功,但我对如何将它与AutoFixture结合使用感到困惑.

我的下面的代码显示了我想要完成的一系列事情,但我的主要目标是完成以下场景:测试方法的功能.

  1. 我希望用随机值调用构造函数(除了一个 - 请阅读第2点).
  2. 无论是在施工期间还是以后,我都希望改变房产的价值 - Data.
  3. 接下来打电话Execute并确认结果

我想要尝试的测试是:"should_run_GetCommand_with_provided_property_value"

任何有关如何使用NSubstitue和AutFixture的文章的帮助或参考都会很棒.

示例代码:

using FluentAssertions;
using NSubstitute;
using Ploeh.AutoFixture;
using Ploeh.AutoFixture.AutoNSubstitute;
using Xunit;

namespace RemotePlus.Test
{
    public class SimpleTest
    {
        [Fact]
        public void should_set_property_to_sepecified_value()
        {
            var sut = Substitute.For<ISimple>();
            sut.Data.Returns("1,2");

            sut.Data.Should().Be("1,2");
        }

        [Fact]
        public void should_run_GetCommand_with_provided_property_value()
        {
            /* TODO:  
             * How do I create a constructor with AutoFixture and/or NSubstitute such that:
             *   1.  With completely random values.
             *   2.  With one or more values specified.
             *   3.  Constructor that has FileInfo as one of the objects.
             * 
             * After creating the constructor:
             *   1.  Specify the value for what a property value should be - ex: sut.Data.Returns("1,2");
             *   2.  Call "Execute" and verify the result for "Command"
             * 
             */
            // Arrange
            var fixture = new Fixture().Customize(new AutoNSubstituteCustomization());
//            var sut = fixture.Build<Simple>().Create();  // Not sure if I need Build or Freeze            
            var sut = fixture.Freeze<ISimple>();  // Note: I am using a Interface here, but would like to test the Concrete class
            sut.Data.Returns("1,2");

            // Act
            sut.Execute();

            // Assert (combining multiple asserts just till I understand how to use NSubstitue and AutoFixture properly
//            sut.Received().Execute();
            sut.Data.Should().Be("1,2");
            sut.Command.Should().Be("1,2,abc");
            // Fails with : FluentAssertions.Execution.AssertionFailedExceptionExpected string to be "1,2,abc" with a length of 7, but "" has a length of 0.

        }
    }

    public class Simple : ISimple
    {

        // TODO: Would like to make this private and use the static call to get an instance
        public Simple(string inputFile, string data)
        {
            InputFile = inputFile;
            Data = data;

            // TODO: Would like to call execute here, but not sure how it will work with testing.
        }

        // TODO: Would like to make this private
        public void Execute()
        {
            GetCommand();
            // Other private methods
        }

        private void GetCommand()
        {
            Command = Data + ",abc";            
        }

        public string InputFile { get; private set; }
        public string Data { get; private set; }

        public string Command { get; private set; }


        // Using this, so that if I need I can easliy switch to a different concrete class
        public ISimple GetNewInstance(string inputFile, string data)
        {
            return new Simple(inputFile, data);
        }

    }

    public interface ISimple
    {
        string InputFile { get; }   // TODO: Would like to use FileInfo instead, but haven't figured out how to test.  Get an error of FileNot found through AutoFixture
        string Data { get; }
        string Command { get; }

        void Execute();
    }
}
Run Code Online (Sandbox Code Playgroud)

for*_*rir 6

我还没有真正使用过AutoFixture,但基于一些阅读和一些反复试验,我认为你错误地解释了它将会做什么,不会为你做什么.在基本级别,它将允许您创建一个对象图,根据对象构造函数为您填充值(可能还有属性,但我没有考虑过).

使用NSubstitute集成不会使您的类的所有成员进入NSubstitute实例.相反,它为fixture框架提供了创建抽象/接口类型作为替换的能力.

查看您尝试创建的类,构造函数需要两个string参数.这些都不是抽象类型或接口,因此AutoFixture将为您生成一些值并将它们传入.这是AutoFixture的默认行为,并基于@Mark Seemann在评论中链接到的答案这是设计.他在那里提出了各种各样的工作,你可以实施,如果这对你来说真的很重要,我在此不再赘述.

您已在评论中指出您确实希望将a传递给FileInfo构造函数.这导致AutoFixture成为问题,因为它的构造函数接受一个字符串,因此AutoFixture正在向它提供一个随机生成的字符串,这是一个不存在的文件,因此您会收到错误.尝试隔离测试似乎是件好事,因此NSubstitute可能对此有用.考虑到这一点,我建议你可能想要重写你的类并测试这样的东西:

首先为FileInfo类创建一个包装器(注意,根据您正在做的事情,您可能希望实际从FileInfo中包装所需的方法,而不是将其作为属性公开,以便您实际上可以将自己与文件系统隔离但是这将暂时做到):

public interface IFileWrapper {
    FileInfo File { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在你的ISimple界面中使用它而不是string(通知我已经删除了Execute,因为你似乎不想在那里):

public interface ISimple {
    IFileWrapper InputFile { get; }   
    string Data { get; }
    string Command { get; }
}
Run Code Online (Sandbox Code Playgroud)

写入Simple实现接口(我没有处理你的私有构造函数问题,或者你在构造函数中调用Execute):

public class Simple : ISimple {

    public Simple(IFileWrapper inputFile, string data) {
        InputFile = inputFile;
        Data = data;
    }

    public void Execute() {
        GetCommand();
        // Other private methods
    }

    private void GetCommand() {
        Command = Data + ",abc";
    }

    public IFileWrapper InputFile { get; private set; }
    public string Data { get; private set; }

    public string Command { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

然后测试:

public void should_run_GetCommand_with_provided_property_value() {
    // Arrange
    var fixture = new Fixture().Customize(new AutoNSubstituteCustomization());

    // create and inject an instances of the IFileWrapper class so that we 
    // can setup expectations
    var fileWrapperMock = fixture.Freeze<IFileWrapper>();

    // Setup expectations on the Substitute.  Note, this isn't needed for
    // this test, since the sut doesn't actually use inputFile, but I've
    // included it to show how it works...
    fileWrapperMock.File.Returns(new FileInfo(@"c:\pagefile.sys"));


    // Create the sut.  fileWrapperMock will be injected as the inputFile
    // since it is an interface, a random string will go into data
    var sut = fixture.Create<Simple>();

    // Act
    sut.Execute();


    // Assert - Check that sut.Command has been updated as expected
    Assert.AreEqual(sut.Data + ",abc", sut.Command);

    // You could also test the substitute is don't what you're expecting
    Assert.AreEqual("pagefile.sys", sut.InputFile.File.Name);
}
Run Code Online (Sandbox Code Playgroud)

我上面没有使用流利的断言,但你应该能够翻译......