Domanda

Ho incontrato una lezione durante il mio lavoro che assomiglia a questo:

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

Il problema è che ho del codice che accede a A, B e C e chiama il metodo getomeValue () (ora, direi che questo non è un buon design, ma a volte le mie mani sono legate ;-)). Voglio creare un dombo di questo oggetto, che, allo stesso tempo, ha A, B e C impostato su alcuni valori. Quindi, quando uso MOQ come tale:

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

Permettimi di configurare un risultato sul metodo getomeValue (), ma tutte le proprietà sono impostate su null (e impostarle tutte utilizzando setup () è piuttosto ingombra Esempio semplificato).

Quindi, d'altra parte, usando AutoFixture in questo modo:

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

Mi lascia senza la capacità di stupire una chiamata al metodo getomeValue ().

Esiste un modo per combinare i due, per avere valori anonimi e la capacità di configurare i risultati delle chiamate?

Modificare

Sulla base della risposta di NEMESV, ho derivato il seguente metodo di utilità (spero di averlo capito bene):

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;
}
È stato utile?

Soluzione

Questo è In realtà è possibile fare con l'autofixture, ma richiede un po 'di modifica. I punti di estensibilità sono tutti lì, ma ammetto che in questo caso la soluzione non è particolarmente rilevabile.

Diventa ancora più difficile se vuoi che funzioni con tipi nidificati/complessi.

dato che MyObject classe sopra, così come questa MyParent classe:

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

    public string Text { get; set; }
}

Questi test unitari passano tutti:

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

Cosa c'è nella fintacustomizzazione? Questo:

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

Il MockPostprocessor, MockConstructorQuery e MockRelay Le classi sono definite in Estensione automobilistica Per automaticamente, quindi dovrai aggiungere un riferimento a questa libreria. Tuttavia, si noti che non è necessario aggiungere il AutoMoqCustomization.

Il AutoExceptMoqPropertiesCommand La classe è anche costruita su misura per l'occasione:

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

Questa soluzione fornisce una soluzione generale alla domanda. Tuttavia, non è stato ampiamente testato, quindi mi piacerebbe ricevere feedback.

Altri suggerimenti

Probabilmente c'è un perché, ma funziona:

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);
//...

Inizialmente ho provato a usare fixture.Register(() => moq.Object); invece di fixture.Customize ma registra la funzione creatore con OmitAutoProperties() Quindi non funzionerebbe per te.

As of 3.20.0, you can use AutoConfiguredMoqCustomization. This will automatically configure all mocks so that their members' return values are generated by AutoFixture.

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);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top