Search code examples
c#moqautofixtureautomoq

Using AutoFixture with Moq?


Using this structure.

public class User
{
    public string FirstName { get; set; }
    public string MiddleName { get; set; }
    public string LastName { get; set; }
    public virtual bool IsAdministrator()
    {
        return false;
    }
}

Is it possible to combine AutoFixture and Moq to accomplish the following?

  1. Ensure User.FirstName is auto-generated
  2. Ensure User.LastName is always "Smith" (literal)
  3. Ensure User.MiddleName is not populated (default)
  4. Ensure User.IsAdministrator() returns True
  5. Verify IsAdministrator() was called.

I know this seems so simple. Here's what I tried using AutoMoq.

var config = new AutoMoqCustomization() 
{ 
    ConfigureMembers = true 
};

var fixture = new AutoFixture.Fixture();
fixture.Customize(config);

fixture.Freeze<Mock<User>>()
    .Setup(x => x.IsAdministrator())
    .Returns(true);

var model = fixture.Build<User>()
    .With(x => x.LastName, "Smith")
    .Without(x => x.MiddleName)
    .Create();

But that is clearly wrong. :( I am sure the syntax is simple. Thank you for any help.

My goal: create an object with BOTH filled properties and mocked methods.


Solution

  • The following achieves the requested goals

    public void AutoFixture_Should_Fill_Poperties_And_Mock_Methods() {
        //Arrange
        AutoMoqCustomization config = new AutoMoqCustomization() {
            ConfigureMembers = true
        };
    
        IFixture fixture = new AutoFixture.Fixture().Customize(config);
    
        Mock<User> mock = new Mock<User>();
        mock
            .Setup(x => x.IsAdministrator())
            .Returns(true);
    
        User model = fixture.Build<User>()
            .FromSeed(s => mock.Object) //<-- 
            .With(x => x.LastName, "Smith")
            .Without(x => x.MiddleName)
            .Create();
    
        //Assert
    
        //Ensure User.FirstName is auto-generated
        model.FirstName.Should().NotBeNullOrEmpty();
        //Ensure User.LastName is always "Smith" (literal)
        model.LastName.Should().Be("Smith");
        //Ensure User.MiddleName is not populated (default)
        model.MiddleName.Should().BeNull();
        //Ensure User.IsAdministrator() returns True
        model.IsAdministrator().Should().BeTrue();
        //Verify IsAdministrator() was called.
        mock.Verify(_ => _.IsAdministrator());
    }
    

    An observation was that when using

    Mock<User> mock = fixture.Freeze<Mock<User>>();
    

    if would still populate the MiddleName property even though it was explicitly told Without for the build, but worked as desired when using

    Mock<User> mock = new Mock<User>();
    

    But if you were to explicitly set the member to return null

    Mock<User> mock = fixture.Freeze<Mock<User>>(); // new Mock<User>();
    mock
        .Setup(x => x.IsAdministrator())
        .Returns(true);
    
    User model = fixture.Build<User>()
        .FromSeed(s => mock.Object)
        .With(x => x.LastName, "Smith")
        .With(x => x.MiddleName, (string)null) //<--
        .Create();
    

    It worked.

    Here is another variation of the test with a subject class that depends on the model to be injected.

    [Test]
    public void AutoFixture_Should_Fill_Poperties_And_Mock_Methods() {
        //Arrange
        AutoMoqCustomization config = new AutoMoqCustomization() {
            ConfigureMembers = true
        };
    
        IFixture fixture = new AutoFixture.Fixture().Customize(config);
    
        Mock<User> mock = new Mock<User>();
        mock
            .Setup(x => x.IsAdministrator())
            .Returns(true);
    
        User model = fixture.Freeze<User>(c => c
            .FromSeed(s => mock.Object)
            .With(x => x.LastName, "Smith")
            .With(x => x.MiddleName, (string)null)
        );
    
        //Act
        Subject subject = fixture.Create<Subject>();
        bool actual = subject.Act();
    
        //Assert
    
        //Ensure User.FirstName is auto-generated
        model.FirstName.Should().NotBeNullOrEmpty();
        //Ensure User.LastName is always "Smith" (literal)
        model.LastName.Should().Be("Smith");
        //Ensure User.MiddleName is not populated (default)
        model.MiddleName.Should().BeNull();
        //Ensure User.IsAdministrator() returns True
        actual.Should().BeTrue();
        //model.IsAdministrator().Should().BeTrue();
        //Verify IsAdministrator() was called.
        mock.Verify(_ => _.IsAdministrator());
    }
    
    
    public class Subject {
        private User model;
    
        public Subject(User model) {
            this.model = model;
        }
    
        public bool Act() => model.IsAdministrator();
    }