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

C# 如何使用NSubstitute和/或AutoFixture测试具体类,c#,unit-testing,autofixture,nsubstitute,C#,Unit Testing,Autofixture,Nsubstitute,我希望通过使用AutoFixture和NSubstitue,我可以充分利用它们各自提供的功能。我已经成功地单独使用了NSubstitute,但我完全不知道如何将它与AutoFixture结合使用 下面的代码显示了我试图完成的一系列事情,但我的主要目标是完成以下场景:测试方法的功能 我希望使用随机值调用构造函数(除了一个-请阅读第2点) 无论是在构造期间还是以后,我都希望更改属性的值-数据 下一次调用执行并确认结果 我正在尝试进行的测试是:“是否应该使用\u提供的\u属性\u值运行\u GetCo

我希望通过使用AutoFixture和NSubstitue,我可以充分利用它们各自提供的功能。我已经成功地单独使用了NSubstitute,但我完全不知道如何将它与AutoFixture结合使用

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

  • 我希望使用随机值调用构造函数(除了一个-请阅读第2点)
  • 无论是在构造期间还是以后,我都希望更改属性的值-
    数据
  • 下一次调用执行并确认结果
  • 我正在尝试进行的测试是:“是否应该使用\u提供的\u属性\u值运行\u GetCommand\u?”

    任何关于如何使用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();
        }
    }
    
    使用FluentAssertions;
    使用替代品;
    使用Ploeh.AutoFixture;
    使用Ploeh.AutoFixture.AutoNSubstitute;
    使用Xunit;
    命名空间RemotePlus.Test
    {
    公共类SimpleTest
    {
    [事实]
    public void应\u将\u属性\u设置为\u单独指定的\u值()
    {
    var sut=替换为();
    sut.数据返回(“1,2”);
    sut.Data.Should()应为(“1,2”);
    }
    [事实]
    public void应使用\u提供的\u属性\u值()运行\u GetCommand\u
    {
    /*待办事项:
    *如何使用AutoFixture和/或NSSubstitute创建构造函数,以便:
    *1.具有完全随机值。
    *2.指定了一个或多个值。
    *3.将FileInfo作为对象之一的构造函数。
    * 
    *创建构造函数后:
    *1.指定属性值的值-例如:sut.Data.Returns(“1,2”);
    *2.调用“执行”并验证“命令”的结果
    * 
    */
    //安排
    var fixture=new fixture().Customize(new AutoNSubstituteCustomization());
    //var sut=fixture.Build().Create();//不确定是否需要生成或冻结
    var sut=fixture.Freeze();//注意:我在这里使用一个接口,但想测试具体类
    sut.数据返回(“1,2”);
    //表演
    sut.Execute();
    //Assert(在我理解如何正确使用NSubstitue和AutoFixture之前,组合多个Assert)
    //sut.Received().Execute();
    sut.Data.Should()应为(“1,2”);
    sut.Command.Should()是(“1,2,abc”);
    //失败:FluentAssertions.Execution.AssertionFailedExceptionExpected字符串为长度为7的“1,2,abc”,但“”的长度为0。
    }
    }
    公共类Simple:ISimple
    {
    //TODO:要将此设置为私有并使用静态调用获取实例吗
    公共简单(字符串输入文件、字符串数据)
    {
    InputFile=InputFile;
    数据=数据;
    //TODO:希望在此处调用execute,但不确定它将如何与测试一起工作。
    }
    //TODO:我想把这件事保密
    public void Execute()
    {
    GetCommand();
    //其他私人方法
    }
    私有void GetCommand()
    {
    命令=数据+“,abc”;
    }
    公共字符串输入文件{get;private set;}
    公共字符串数据{get;private set;}
    公共字符串命令{get;private set;}
    //使用它,以便在需要时可以轻松切换到不同的具体类
    公共ISimple GetNewInstance(字符串输入文件、字符串数据)
    {
    返回新的简单文件(输入文件、数据);
    }
    }
    公共接口很简单
    {
    string InputFile{get;}//TODO:想改用FileInfo,但还没有找到测试方法。通过AutoFixture获取FileNot found的错误
    字符串数据{get;}
    字符串命令{get;}
    void Execute();
    }
    }
    
    我实际上没有太多使用AutoFixture,但基于一些阅读和一些尝试和错误,我认为您误解了它对您有什么好处,也不会有什么好处。在基本层面上,它可以让您创建一个对象图,根据对象构造函数为您填充值(可能还有财产,但我还没有调查过)

    使用NSubstitute集成并不会将类的所有成员都转换为NSubstitute实例。相反,它使fixture框架能够创建抽象/接口类型作为替代

    查看您试图创建的类,构造函数采用两个
    string
    参数。这两个参数都不是抽象类型,也不是接口,因此AutoFixture只会为您生成一些值并将其传递给您。这是AutoFixture的默认行为,并基于此i他在那里提出了各种各样的解决办法,如果这对你来说真的很重要的话,你可以实施,我在这里不再重复

    您在评论中指出,您确实希望将
    FileInfo
    传递到构造函数中。这会导致AutoFixture出现问题,因为它的构造函数接受字符串,因此AutoFixture会向它提供随机生成的字符串,这是一个不存在的文件,因此会出现错误。这似乎是一件好事尝试隔离以进行测试,因此NSubstitute可能会有用。考虑到这一点,我建议您可能需要重写类并测试以下内容:

    [Test]
    public void should_run_GetCommand_with_provided_property_value() {
        // Arrange
        var inputFile = "someInputFile";
        var data = "1,2";
        var expectedCommand = "1,2,abc";
    
    
        // Act
        // Note, I'm calling the static method to create your instance
        var sut = Simple.GetNewInstance(inputFile, data);
    
        // Assert
        Assert.AreEqual(inputFile, sut.InputFile);
        Assert.AreEqual(data, sut.Data);
        Assert.AreEqual(expectedCommand, sut.Command);
    }
    
    public interface IMyObjectFactory {
        ISimple CreateSimple(string inputFile, string data);
    }
    
    public class MyObjectFactory {
        ISimple CreateSimple(string inputFile, string data) {
            var simple =  new Simple(inputFile, data);
            simple.Execute();
            return simple;
        }
    }
    
    首先,为
    FileInfo
    类创建一个包装器(注意,根据您的实际操作,您可能需要
    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; }
    }
    
    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);
    }
    
    using FluentAssertions;
    using NSubstitute;
    using Ploeh.AutoFixture;
    using Ploeh.AutoFixture.AutoNSubstitute;
    using Xunit;
    using Xunit.Abstractions;
    
    namespace Try.xUnit.Tests
    {
        public class TestingMethodCalls
        {
            private readonly ITestOutputHelper _output;
    
            public TestingMethodCalls(ITestOutputHelper output)
            {
                _output = output;
            }
    
    
            [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 (Skip="Don't quite understand how to use AutoFixture and NSubstitue together")]
            public void should_run_GetCommand_with_provided_property_value_old()
            {
                /* 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.
            }
    
            /* Explanation:
             * Create a construtor without any arguments.
             *      Had to create a parameterless constructor just for testing purposes (would like to improve on this)
             * Specify a default value for the desired method or property.
             *      It is necessary that the property or method has to be virtual.
             *      To specify that the based mehod should be call use the "DoNotCallBase" before the "Returns" call
             */ 
            [Fact]
            public void should_run_GetCommand_with_provided_Method_value()
            {
                // Arrange
                var sut = Substitute.ForPartsOf<Simple>();
                sut.When(x => x.GetData()).DoNotCallBase();
                sut.GetData().Returns("1,2");
    
                // Act
                sut.Execute();
    
                // Assert
                sut.Received().GetData();
    
                sut.Data.Should().Be("1,2");
                sut.Command.Should().Be("1,2,abc");
            }
    
            [Fact]
            public void should_run_GetCommand_with_provided_Property_value()
            {
    
                // Arrange
                var sut = Substitute.ForPartsOf<Simple>();
                sut.When(x => { var data = x.Data; }).DoNotCallBase();
                sut.Data.Returns("1,2");
    
                // Act
                sut.Execute();
    
                // Assert
                sut.Received().GetData();
                _output.WriteLine(sut.Command);
    
                sut.Data.Should().Be("1,2");
                sut.Command.Should().Be("1,2,abc");
            }
    
        }
    
        public class Simple : ISimple
        {
            public Simple(){}
    
            // TODO: Would like to make this private and use the static call to get an instance
            public Simple(string inputFile, string data)
            {
                InputFile = inputFile;
                InputData = data;
    
                // TODO: Would like to call execute here, but not sure how it will work with testing.
            }
    
            public virtual string GetData()
            {
                // Assume some manipulations are done
                return InputData;
            }
    
            // TODO: Would like to make this private
            public void Execute()
            {
                Data = GetData();
                GetCommand();
                // Other private methods
            }
    
            private void GetCommand()
            {
                Command = Data + ",abc";            
            }
    
            string InputData { get; set; }
    
            public string InputFile { get; private set; }
    
    
            public virtual 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();
        }
    
    }
    
    public void should_set_property_to_sepecified_value()
    {
        var sut = Substitute.For<ISimple>();
        sut.Data.Returns("1,2");
    
        sut.Data.Should().Be("1,2");
    }
    
    public interface ISimple {
        string InputFile { get; }   
        string Data { get; }
        string Command { get; }
    }
    
    public class Simple : ISimple {
        private Simple(string inputFile, string data) {
            InputFile = inputFile;
            Data = data;
        }
    
        private void Execute() {
            GetCommand();
        }
    
        private void GetCommand() {
            Command = Data + ",abc";
        }
    
        public string InputFile { get; private set; }
        public string Data { get; private set; }
    
        public string Command { get; private set; }
    
    
        // Note.. GetNewInstance is static and it calls the Execute method
        static public ISimple GetNewInstance(string inputFile, string data) {
            var simple =  new Simple(inputFile, data);
            simple.Execute();
            return simple;
        }
    
    }
    
    [Test]
    public void should_run_GetCommand_with_provided_property_value() {
        // Arrange
        var inputFile = "someInputFile";
        var data = "1,2";
        var expectedCommand = "1,2,abc";
    
    
        // Act
        // Note, I'm calling the static method to create your instance
        var sut = Simple.GetNewInstance(inputFile, data);
    
        // Assert
        Assert.AreEqual(inputFile, sut.InputFile);
        Assert.AreEqual(data, sut.Data);
        Assert.AreEqual(expectedCommand, sut.Command);
    }
    
    public interface IMyObjectFactory {
        ISimple CreateSimple(string inputFile, string data);
    }
    
    public class MyObjectFactory {
        ISimple CreateSimple(string inputFile, string data) {
            var simple =  new Simple(inputFile, data);
            simple.Execute();
            return simple;
        }
    }