简体   繁体   中英

How to use NSubstitute and/or AutoFixture to test a concrete class

I was hoping that by using AutoFixture and NSubstitue, I could use the best of what each have to provide. I have had some success using NSubstitute on its own, but I am completely confused on how to use it in combination with AutoFixture.

My code below shows a set of things I am trying to accomplish, but my main goal here is to accomplish the following scenario: Test the functionality of a method.

  1. I expect the constructor to be called with random values (except maybe one - please read point 2.).
  2. Either during construction or later, I want to change value of a property - Data .
  3. Next call Execute and confirm the results

The test that I am trying to get working is: "should_run_GetCommand_with_provided_property_value"

Any help or reference to an article that shows how NSubstitue and AutFixture SHOULD be used, would be great.

Sample Code:

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();
    }
}

I haven't really used AutoFixture much, but based on some reading and a bit of trial and error, I think you're misinterpreting what it will and won't do for you. At a basic level, it'll let you create a graph of objects, filling in values for you based around the objects constructors (and possibly properties but I haven't looked into that).

Using the NSubstitute integration doesn't make all of the members of your class into NSubstitute instances. Instead, it gives the fixture framework the ability to create abstract / interface types as Substitutes.

Looking at the class you're trying to create, the constructor takes two string parameters. Neither of these is an abstract type, or an interface so AutoFixture is just going to generate some values for you and pass them in. This is AutoFixture's default behaviour and based on the answer linked to by @Mark Seemann in the comments this is by design. There are various work arounds proposed by him there that you can implement if it's really important for you to do so, which I won't repeat here.

You've indicated in your comments that you really want to pass a FileInfo into your constructor. This is causing AutoFixture a problem, since its constructor takes a string and consequently AutoFixture is supplying a random generated string to it, which is a non-existent file so you get an error. This seems like a good thing to try to isolate for testing so is something that NSubstitute might be useful for. With that in mind, I'm going to suggest that you might want to rewrite your classes and test something like this:

First up create a wrapper for the FileInfo class (note, depending on what you're doing you might want to actually wrap the methods from FileInfo that you want, rather than exposing it as a property so that you can actually isolate yourself from the filesystem but this will do for the moment):

public interface IFileWrapper {
    FileInfo File { get; set; }
}

Use this in your ISimple interface instead of a string (notice I've removed Execute since you don't seem to want it there):

public interface ISimple {
    IFileWrapper InputFile { get; }   
    string Data { get; }
    string Command { get; }
}

Write Simple to implement the interface (I haven't tackled your private constructor issue, or your call to Execute in the constructor):

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; }
}

And then the test:

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);
}

I'm not using fluent asserts above, but you should be able to translate...

I actually managed to find a solution by realizing that I don't need to use AutoFixture for my current scenario.

I had to make some changes to my code though:

  1. Added a default constructor.
  2. Marked the methods and properties I want to provide a default value for as "virtual".

Ideally, I do not want to do these things, but it is enough to get me started and keep me moving forward for now.

Links that helped a lot:

Modified code:

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();
    }

}

I'm posting this as a separate answer because it's more a critique of approach, than direct answer to your original question. In my other answer I've tried to directly answer your AutoFixture/NSubstitute questions assuming that you are currently trying to learn these to frameworks.

As it stands, you don't really need to use either of these frameworks to achieve what you are doing and in some ways it's easier not to. Looking at this test:

public void should_set_property_to_sepecified_value()
{
    var sut = Substitute.For<ISimple>();
    sut.Data.Returns("1,2");

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

This isn't actually testing your class at all (other than compilation checks), really you're testing NSubstitute. You're checking that if you tell NSubstitute to return a value for a property that it does.

Generally speaking, try to avoid mocking the class that you're testing. If you need to do it, then there is a good chance that you need to rethink your design. Mocking is really useful for supplying dependencies into your class that you can control in order to influence the behaviour of your class. If you start modifying the behaviour of the class you're testing using mocks then it's very easy to get confused about what you're actually testing (and to create very brittle tests).

Because you're dealing with basic types and not nested objects, at the moment it's easy to create + test your objects without using something like AutoFixture/NSubstitute. You code could look like this, which seems to be closer to what you're hoping for:

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;
    }

}

And your test would look like this:

[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);
}

I've left the Execute outside of the objects constructor because it feels a bit like it's going to be doing too much. I'm not a huge fan of doing a lot other than basic setup in constructors particularly if there's a chance you might end up calling virtual methods . I've also made GetNewInstance static so that it can be called directly (otherwise you have to create a Simple to call GetNewInstance on it which seems wrong)...

Whilst I've shown above how your code could work as you want it so, I'd suggest that you might want to change the Simple constructor to internal, rather than private. This would allow you to create a factory to create the instances. If you had something like this:

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;
    }
}

This allows you to safely constructor objects that need methods called on them. You can also inject substitutes of the IMyObjectFactory that returns a substitute of ISimple in future classes that are dependent on the Simple class. This helps you to isolate your classes from the underlying class behaviour (which might access the file system) and makes it easy for you to stub responses.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM