Fork me on GitHub
Nikos Baxevanis

Dynamic Proxy overriding Equals in AutoFixture Likeness

From version 2.9.0 of AutoFixture, the Likeness class contains a new feature for creating a dynamic proxy that overrides Equals on the destination type.

As an example, we want to compare instances of the following types:

public class DoubleParameterType<T1, T2>
{
    public DoubleParameterType(T1 parameter1, T2 parameter2)
    {
        this.Parameter1 = parameter1;
        this.Parameter2 = parameter2;
    }

    public T1 Parameter1 { get; private set; }
    public T2 Parameter2 { get; private set; }
}

public class SingleParameterType<T>
{
    public SingleParameterType(T parameter)
    {
        this.Parameter = parameter;
    }

    public T Parameter { get; private set; }
}

We can have the following syntax (prior to version 2.9.0):

[Fact]
public void TestWithLikeness()
{
    // Fixture setup
    var value = new DoubleParameterType<int, double>(1, 2.0);

    Likeness<DoubleParameterType<int, double>, SingleParameterType<int>> sut 
        = value.AsSource()
               .OfLikeness<SingleParameterType<int>>();
            
    // Exercise system
    var result = sut.Equals(value);
    // Verify outcome
    Assert.True(result);
}

However, from version 2.9.0 there is also a new CreateProxy method on Likeness which returns a proxy of the destination type overriding Equals with Likeness's instance of IEqualityComparer (the SemanticComparer class):

[Fact]
public void TestWithLikenessProxy()
{
    // Fixture setup
    var value = new DoubleParameterType<int, double>(1, 2.0);
            
    SingleParameterType<int> sut
        = value.AsSource()
               .OfLikeness<SingleParameterType<int>>()
               .CreateProxy();
            
    // Exercise system
    var result = sut.Equals(value);
    // Verify outcome
    Assert.True(result);
}

Below is also an example, where we need to verify that an expectation was met:

public class Bar
{
    public string Zip { get; set; }
}

public class Foo
{
    public Bar Bar { get; private set; }

    public void DoSomething(ISomeContext ctx)
    {
        this.Bar = new Bar { Zip = "12345" };
        ctx.DoSomething(this.Bar);
    }
}

public interface ISomeContext
{
    void DoSomething(object request);
}

[Fact]
public void Test()
{
    var foo = new Foo();
    var ctx = new Mock<ISomeContext>();
    foo.DoSomething(ctx.Object);

    var bar = new Bar().AsSource().OfLikeness<Bar>().CreateProxy();
    bar.Zip = "12345";

    ctx.Verify(x => x.DoSomething(bar));
}

Although the new Bar instance is created inside the DoSomething method, we can pass a proxied Bar instance on the mock's Verify method.

Internally, a custom Proxy Generator was written which also supports types with non-parameterless constructors. In order to create proxies of such types, the values from the source have to be compatible with the parameters on the destination constructor. (The mapping between the two is made possible by using the same semantic heuristics, as the default semantic comparison.)