سؤال

I am trying to use AutoFixture to simplify my life.

How do I mock a class with

  • non-public constructor and
  • non-public Id and
  • static 'Create' method?

Example

public class User
{
    private User(){}

    /// <summary>
    ///     Created by Database...
    /// </summary>
    public long? Id { get; protected set; }

    public string Name { get; protected set; }

    public static User Create(string name)
    {
        var user = new User {Name = name};
        return user;
    }
}

I've tried using a combo of Factory and SpecimenBuilder:

    [Fact]
    public void CreatingUserWithId_Should_Work_UsingFactoryAndSpecimenBuilder()
    {
        IFixture fixture = new Fixture().Customize(new AutoFakeItEasyCustomization());
        fixture.Customizations.Add(new UserBuilder());
        fixture.Customize<User>(o => o.FromFactory(() => User.Create("foo")));

        var user = fixture.Create<User>();

        user.Should().NotBeNull();
        user.Id.Should().HaveValue();
    }

with

public class UserBuilder : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as PropertyInfo;
        if (pi == null) return new NoSpecimen(request);

        // The following code only works for public properties... :-(
        if (pi.Name == "Id" && pi.PropertyType == typeof (long?)) return 42;

        return new NoSpecimen(request);
    }
}

A demo C# solution is available at https://github.com/draptik/MockingStaticCreate

Thankful for any pointers,

Patrick

هل كانت مفيدة؟

المحلول

AF will do the right thing (User.Create() with an anonymous name arg) with no customizations whatsoever.

The only missing bit is setting the Id. Which is a question you'll have to answer for yourself -- how should your consuming code do this in the first place ? When you've decided, you could do fixture.Customize<User>( c => c.FromFactory( User.Create).Do( x => ???)

Perhaps you could consider exposing a ctor that takes an id too. Then you can do a Customize<User> ... GreedyConstructorQuery.

If your ORM is doing some wacky reflection and you like that and/or can't route around it you get to choose whether for your tests you should:

a) do that too - if that's relevant to a given test or set of tests

b) consider that to be something that just works

Regarding mixing mocking and feeding in of values to an Entity or Value object - Don't do that (Mark Seemann's Commands vs Queries article might help you here). The fact that you seem to be needing/wanting to do so makes it seems you're trying to be too ambitions in individual tests - are you finding the simplest thing to test and trying to have a single Assert testing one thing with minimal setup.

Go buy the GOOS book - it'll help you figure out ways of balancing these kinds of trade-offs.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top