Search code examples
c#mockingmoqautofixtureanonymous-objects

Creating a hybrid of a mock and an anonymous object using e.g. Moq and AutoFixture?


I encountered a class during my work that looks like this:

public class MyObject
{
  public int? A {get; set;}
  public int? B {get; set;}
  public int? C {get; set;}
  public virtual int? GetSomeValue()
  {
    //simplified behavior:
    return A ?? B ?? C;
  }  
}

The issue is that I have some code that accesses A, B and C and calls the GetSomeValue() method (now, I'd say this is not a good design, but sometimes my hands are tied ;-)). I want to create a mock of this object, which, at the same time, has A, B and C set to some values. So, when I use moq as such:

var m = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock };

lets me setup a result on GetSomeValue() method, but all the properties are set to null (and setting up all of them using Setup() is quite cumbersome, since the real object is a nasty data object and has more properties than in above simplified example).

So on the other hand, using AutoFixture like this:

var fixture = new Fixture();
var anyMyObject = fixture.CreateAnonymous<MyObject>();

Leaves me without the ability to stup a call to GetSomeValue() method.

Is there any way to combine the two, to have anonymous values and the ability to setup call results?

Edit

Based on nemesv's answer, I derived the following utility method (hope I got it right):

public static Mock<T> AnonymousMock<T>() where T : class
{
  var mock = new Mock<T>();
  fixture.Customize<T>(c => c.FromFactory(() => mock.Object));
  fixture.CreateAnonymous<T>();
  fixture.Customizations.RemoveAt(0);
  return mock;
}

Solution

  • This is actually possible to do with AutoFixture, but it does require a bit of tweaking. The extensibility points are all there, but I admit that in this case, the solution isn't particularly discoverable.

    It becomes even harder if you want it to work with nested/complex types.

    Given the MyObject class above, as well as this MyParent class:

    public class MyParent
    {
        public MyObject Object { get; set; }
    
        public string Text { get; set; }
    }
    

    these unit tests all pass:

    public class Scenario
    {
        [Fact]
        public void CreateMyObject()
        {
            var fixture = new Fixture().Customize(new MockHybridCustomization());
    
            var actual = fixture.CreateAnonymous<MyObject>();
    
            Assert.NotNull(actual.A);
            Assert.NotNull(actual.B);
            Assert.NotNull(actual.C);
        }
    
        [Fact]
        public void MyObjectIsMock()
        {
            var fixture = new Fixture().Customize(new MockHybridCustomization());
    
            var actual = fixture.CreateAnonymous<MyObject>();
    
            Assert.NotNull(Mock.Get(actual));
        }
    
        [Fact]
        public void CreateMyParent()
        {
            var fixture = new Fixture().Customize(new MockHybridCustomization());
    
            var actual = fixture.CreateAnonymous<MyParent>();
    
            Assert.NotNull(actual.Object);
            Assert.NotNull(actual.Text);
            Assert.NotNull(Mock.Get(actual.Object));
        }
    
        [Fact]
        public void MyParentIsMock()
        {
            var fixture = new Fixture().Customize(new MockHybridCustomization());
    
            var actual = fixture.CreateAnonymous<MyParent>();
    
            Assert.NotNull(Mock.Get(actual));
        }
    }
    

    What's in MockHybridCustomization? This:

    public class MockHybridCustomization : ICustomization
    {
        public void Customize(IFixture fixture)
        {
            fixture.Customizations.Add(
                new MockPostprocessor(
                    new MethodInvoker(
                        new MockConstructorQuery())));
            fixture.Customizations.Add(
                new Postprocessor(
                    new MockRelay(t =>
                        t == typeof(MyObject) || t == typeof(MyParent)),
                    new AutoExceptMoqPropertiesCommand().Execute,
                    new AnyTypeSpecification()));
        }
    }
    

    The MockPostprocessor, MockConstructorQuery and MockRelay classes are defined in the AutoMoq extension to AutoFixture, so you'll need to add a reference to this library. However, note that it's not required to add the AutoMoqCustomization.

    The AutoExceptMoqPropertiesCommand class is also custom-built for the occasion:

    public class AutoExceptMoqPropertiesCommand : AutoPropertiesCommand<object>
    {
        public AutoExceptMoqPropertiesCommand()
            : base(new NoInterceptorsSpecification())
        {
        }
    
        protected override Type GetSpecimenType(object specimen)
        {
            return specimen.GetType();
        }
    
        private class NoInterceptorsSpecification : IRequestSpecification
        {
            public bool IsSatisfiedBy(object request)
            {
                var fi = request as FieldInfo;
                if (fi != null)
                {
                    if (fi.Name == "__interceptors")
                        return false;
                }
    
                return true;
            }
        }
    }
    

    This solution provides a general solution to the question. However, it hasn't been extensively tested, so I'd love to get feedback on it.