Création d'un hybride d'une maquette et un objet anonyme en utilisant par exemple Moq et AutoFixture?

StackOverflow https://stackoverflow.com/questions/9341413

Question

Je rencontre une classe au cours de mon travail qui ressemble à ceci:

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

La question est que j'ai un code qui accède à A, B et C et appelle la méthode GetSomeValue () (maintenant, je dirais que ce n'est pas une bonne conception, mais parfois mes mains sont liées ;-)). Je veux créer une maquette de cet objet, qui, en même temps, a A, B et C réglé sur certaines valeurs. Donc, quand je l'utilise moq en tant que tel:

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

me permet de configurer un résultat sur la méthode GetSomeValue (), mais toutes les propriétés sont définies sur null (et la mise en place tous les utiliser le programme d'installation () est assez lourd, puisque l'objet réel est un objet de données méchant et a plus de propriétés que dans exemple ci-dessus simplifiée).

d'autre part, en utilisant AutoFixture comme ceci:

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

Feuilles moi sans la capacité de Stup un appel à la méthode GetSomeValue ().

Est-il possible de combiner les deux, pour avoir des valeurs anonymes et la possibilité de résultats d'appel de configuration?

Modifier

Sur la base de la réponse de nemesv, je dérivé de la méthode d'utilité suivante (espérons que je me trompe pas):

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;
}
Était-ce utile?

La solution

est en fait possible de le faire avec AutoFixture, mais il nécessite un peu de peaufinage. Les points sont là extensibilité, mais j'avoue que, dans ce cas, la solution est particulièrement découvrable.

Il devient encore plus difficile si vous voulez travailler avec des types imbriqués / complexes.

Compte tenu de la classe MyObject ci-dessus, ainsi que cette classe MyParent:

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

    public string Text { get; set; }
}

ces unités teste tous passe:

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

Qu'est-ce qui est en MockHybridCustomization? Ceci:

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

Les MockPostprocessor, les classes de MockConstructorQuery et MockRelay sont définis dans la AutoMoq extension à AutoFixture, vous aurez donc besoin d'ajouter une référence à cette bibliothèque. Toutefois, notez que ce n'est pas nécessaire d'ajouter le AutoMoqCustomization.

La classe AutoExceptMoqPropertiesCommand est également construit sur mesure pour l'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;
        }
    }
}

Cette solution fournit une solution générale à la question. Cependant, il n'a pas été testé, donc j'aimerais obtenir des commentaires à ce sujet.

Autres conseils

Il y a probablement une meilleure pourquoi, mais cela fonctionne:

var fixture = new Fixture();
var moq = new Mock<MyObject>() { DefaultValue = DefaultValue.Mock };
moq.Setup(m => m.GetSomeValue()).Returns(3);

fixture.Customize<MyObject>(c => c.FromFactory(() => moq.Object));

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

Assert.AreEqual(3, anyMyObject.GetSomeValue());
Assert.IsNotNull(anyMyObject.A);
//...

Dans un premier temps j'ai essayé d'utiliser fixture.Register(() => moq.Object); au lieu de fixture.Customize mais il enregistre la fonction de créateur avec OmitAutoProperties() il ne fonctionnerait pas pour vous cas.

Au 3.20.0, vous pouvez utiliser AutoConfiguredMoqCustomization. Cela configurera automatiquement tous les simulacres de sorte que sont générés par AutoFixture les valeurs de retour de leurs membres.

var fixture = new Fixture().Customize(new AutoConfiguredMoqCustomization());

var mock = fixture.Create<Mock<MyObject>>();

Assert.NotNull(mock.Object.A);
Assert.NotNull(mock.Object.B);
Assert.NotNull(mock.Object.C);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top