web-dev-qa-db-fra.com

Comment se moquer d'une classe qui implémente plusieurs interfaces

Comment se moquer de la classe suivante:

UserRepository : GenericRepository<User>, IUserRepository


public class GenericRepository<TEntity> : IGenericRepository<TEntity> where TEntity : class

J'utilise Moq et je ne sais pas comment gérer correctement plusieurs interfaces.

45
loyalflow

Jetez un oeil à https://github.com/Moq/moq4/wiki/Quickstart

Fonctions avancées

// implementing multiple interfaces in mock
var foo = new Mock<IFoo>();
var disposableFoo = foo.As<IDisposable>();
// now IFoo mock also implements IDisposable :)
disposableFoo.Setup(df => df.Dispose());
69
ShloEmi

Il existe un mécanisme intégré à Moq pour gérer plusieurs interfaces.

Disons que nous avons une interface IFoo et une implémentation de la même Foo. Nous avons également ClientOne qui utilise IFoo.

Nous avons alors une interface IFooBar: IFoo, une implémentation FooBar: Foo, IFooBar et un ClientTwo qui utilise IFooBar.

Lors de la création d'un test de bout en bout pour le système, nous avons un IFooBar, ClientOne et ClientTwo. La fonction As <> () nous permet d'utiliser Mock <IFooBar> comme Mock <IFoo>.

public interface IFoo {
    int Id { get; }
}

public class Foo : IFoo {
    public int Id {
        get { return 1; }
    }
}

public interface IFooBar : IFoo  {
    string Name { get; }
}

public class FooBar : Foo, IFooBar {
    public string Name {
        get { return "AName"; }
    }
}

public class ClientOne {
    private readonly IFoo foo;

    public ClientOne(IFoo foo) {
        this.foo = foo;
    }

    public string Details {
        get { return string.Format("Foo : {0}", foo.Id); }
    }

}

public class ClientTwo {
    private readonly IFooBar fooBar;

    public ClientTwo(IFooBar fooBar) {
        this.fooBar = fooBar;
    }

    public string Details {
        get { return string.Format("Foo : {0}, Bar : {1}", fooBar.Id, fooBar.Name); }
    }

}


[TestMethod]
public void TestUsingBothClients() {

    var fooBarMock = new Mock<IFooBar>();
    var fooMock = fooBarMock.As<IFoo>();

    fooBarMock.SetupGet(mk => mk.Id).Returns(1);
    fooBarMock.SetupGet(mk => mk.Name).Returns("AName");

    var clientOne = new ClientOne(fooMock.Object);
    var clientTwo = new ClientTwo(fooBarMock.Object);

    Assert.AreEqual("Foo : 1", clientOne.Details);
    Assert.AreEqual("Foo : 1, Bar : AName", clientTwo.Details);

}
24
AlanT

Si je comprends bien la question, vous voulez avoir une seule instance fictive de UserRepository, et pour les besoins d'un test, le programme d'installation appelle des méthodes à la fois du IGenericRepository<TEntity> interface et IUserRepository interface.

Vous pouvez implémenter plusieurs interfaces avec une seule instance fictive comme ceci:

var genericRepositoryMock = new Mock<IGenericRepository<User>>();
genericRepositoryMock.Setup(m => m.CallGenericRepositoryMethod()).Returns(false);

var userRepositoryMock = genericRepositoryMock.As<IUserRepository>();
userRepositoryMock.Setup(m => m.CallUserRepositoryMethod()).Returns(true);

Cependant, comme l'a souligné D Stanley, la nécessité de le faire est probablement une indication qu'il y a un défaut dans votre conception.

3
Alex Peck

Vous ne vous moquez pas classes, vous vous moquez interfaces. Dans votre cas, vous pouvez avoir deux moqueries - une qui se moque IUserRepository et une qui se moque IGenericRepository<User>. Ils ne devraient pas nécessairement être le même objet - s'ils DOIVENT être le même objet, cela peut être un défaut de conception.

3
D Stanley