46 votes

Faux DbContext de l'Entity Framework 4.1 pour Tester

Je suis l'aide de ce tutoriel pour Faux mon DbContext et de test: http://refactorthis.wordpress.com/2011/05/31/mock-faking-dbcontext-in-entity-framework-4-1-with-a-generic-repository/

Mais j'ai du modifier le FakeMainModuleContext mise en œuvre pour les utiliser dans mes Contrôleurs:

public class FakeQuestiona2011Context : IQuestiona2011Context
{
    private IDbSet<Credencial> _credencial;
    private IDbSet<Perfil> _perfil;
    private IDbSet<Apurador> _apurador;
    private IDbSet<Entrevistado> _entrevistado;
    private IDbSet<Setor> _setor;
    private IDbSet<Secretaria> _secretaria;
    private IDbSet<Pesquisa> _pesquisa;
    private IDbSet<Pergunta> _pergunta;
    private IDbSet<Resposta> _resposta;

    public IDbSet<Credencial> Credencial { get { return _credencial ?? (_credencial = new FakeDbSet<Credencial>()); } set { } }
    public IDbSet<Perfil> Perfil { get { return _perfil ?? (_perfil = new FakeDbSet<Perfil>()); } set { } }
    public IDbSet<Apurador> Apurador { get { return _apurador ?? (_apurador = new FakeDbSet<Apurador>()); } set { } }
    public IDbSet<Entrevistado> Entrevistado { get { return _entrevistado ?? (_entrevistado = new FakeDbSet<Entrevistado>()); } set { } }
    public IDbSet<Setor> Setor { get { return _setor ?? (_setor = new FakeDbSet<Setor>()); } set { } }
    public IDbSet<Secretaria> Secretaria { get { return _secretaria ?? (_secretaria = new FakeDbSet<Secretaria>()); } set { } }
    public IDbSet<Pesquisa> Pesquisa { get { return _pesquisa ?? (_pesquisa = new FakeDbSet<Pesquisa>()); } set { } }
    public IDbSet<Pergunta> Pergunta { get { return _pergunta ?? (_pergunta = new FakeDbSet<Pergunta>()); } set { } }
    public IDbSet<Resposta> Resposta { get { return _resposta ?? (_resposta = new FakeDbSet<Resposta>()); } set { } }

    public void SaveChanges()
    {
        // do nothing (probably set a variable as saved for testing)
    }
}

Et mon test comme ça:

[TestMethod]
public void IndexTest()
{
    IQuestiona2011Context fakeContext = new FakeQuestiona2011Context();
    var mockAuthenticationService = new Mock<IAuthenticationService>();

    var apuradores = new List<Apurador>
    {
        new Apurador() { Matricula = "1234", Nome = "Acaz Souza Pereira", Email = "acaz@telecom.inf.br", Ramal = "1234" },
        new Apurador() { Matricula = "4321", Nome = "Samla Souza Pereira", Email = "samla@telecom.inf.br", Ramal = "4321" },
        new Apurador() { Matricula = "4213", Nome = "Valderli Souza Pereira", Email = "valderli@telecom.inf.br", Ramal = "4213" }
    };
    apuradores.ForEach(apurador => fakeContext.Apurador.Add(apurador));

    ApuradorController apuradorController = new ApuradorController(fakeContext, mockAuthenticationService.Object);
    ActionResult actionResult = apuradorController.Index();

    Assert.IsNotNull(actionResult);
    Assert.IsInstanceOfType(actionResult, typeof(ViewResult));

    ViewResult viewResult = (ViewResult)actionResult;

    Assert.IsInstanceOfType(viewResult.ViewData.Model, typeof(IndexViewModel));

    IndexViewModel indexViewModel = (IndexViewModel)viewResult.ViewData.Model;

    Assert.AreEqual(3, indexViewModel.Apuradores.Count);
}

Je suis en train de faire?

122voto

Ladislav Mrnka Points 218632

Vous n'êtes malheureusement pas le faire juste parce que l'article est faux. Il prétend qu' FakeContext feront de votre unité de code testable, mais il ne sera pas. Une fois que vous exposez IDbSet ou IQueryable de votre manette et vous faux avec en mémoire la collection que vous ne pouvez jamais être sûr que votre unité de test vraiment des tests de votre code. Il est très facile d'écrire une requête LINQ dans votre contrôleur qui fera passer votre test unitaire (parce qu' FakeContext utilise LINQ-to-Objets), mais échoue au moment de l'exécution (parce que votre contexte réel utilise LINQ-to-Entités). Qui fait tout l'objet de vos tests unitaires inutile.

Mon avis: Ne vous embêtez pas avec faking contexte si vous souhaitez exposer ensembles de contrôleur. Au lieu d'utiliser des tests d'intégration avec le réel de la base de données pour les tests. C'est la seule façon de valider que les requêtes LINQ définie dans le contrôleur de faire ce que vous attendez.

Bien sûr, si vous voulez l'appeler juste ToList ou FirstOrDefault sur vos jeux votre FakeContext vous sera bien utile, mais une fois que vous faites quelque chose de plus complexe, vous pouvez trouver un piège assez vite (il suffit de mettre la chaîne "Ne peut pas être traduit dans un magasin de l'expression" dans Google - tous ces problèmes apparaissent uniquement lorsque vous exécutez Linq-to-entités, mais ils vont passer vos tests avec Linq-to-objets).

C'est assez fréquent question de sorte que vous pouvez vérifier quelques autres exemples:

63voto

brentmckendrick Points 1538

"Vous n'êtes malheureusement pas le faire juste parce que l'article est faux. Il prétend que FakeContext fera de votre unité de code testable, mais il ne sera pas"

Je suis le créateur du blog que vous consultez. Le problème que je vois ici est une mauvaise compréhension des principes de base de N-Couches de tests unitaires. Mon post n'est pas destiné à être utilisé directement pour tester la logique du contrôleur.

Un test unitaire doit faire exactement ce que son nom l'indique et le test de la "Une Unité". Si je suis en train de tester un contrôleur (comme vous le faites ci-dessus) - je oublier tout au sujet de l'accès aux données. Je devrais être de supprimer tous les appels à la base de données de contexte, dans mon esprit, et de les remplacer par une boîte noire à l'appel de la méthode", comme si ces opérations étaient inconnus pour moi. C'est le code autour de ces opérations que je me suis intéressé à l'essai.

Exemple:

Dans mon application MVC, nous utilisons le modèle de référentiel. J'ai un référentiel, dire CustomerRepository : ICustomerRepository, qui va réaliser l'ensemble de ma Clientèle opérations de base de données.

Si je devais tester mes manettes je veux les tests pour tester mon référentiel, ma base de données access, et le contrôleur de la logique elle-même? bien sûr que non! il y a beaucoup de "unités" dans ce pipeline. Ce que vous voulez faire est de créer un faux référentiel qui met en œuvre ICustomerRepository pour vous permettre de tester le contrôleur de logique dans l'isolement.

Ce, au meilleur de ma connaissance ne peut pas être fait sur la base de données de contexte à lui seul. (sauf peut-être pour l'utilisation de Microsoft grains de beauté, que vous pouvez vérifier si vous voulez). C'est tout simplement parce que toutes les requêtes sont effectuées en dehors du contexte de votre contrôleur de classe.

Si j'ai voulu tester le CustomerRepository logique comment dois-je procéder? Le plus simple est d'utiliser un faux cadre. Cela me permettra de faire en sorte que lorsque je vais essayer d'obtenir un client par code, il obtient réellement le client par code et ainsi de suite. Référentiel de méthodes sont très simples et le "Ne peut pas être traduit dans un magasin expression" problème ne sera pas généralement de surface. Si, dans certains rares cas, il peut (parfois à cause mal écrit les requêtes linq) dans ces cas, il est également important d'effectuer des tests d'intégration qui vous permettra de tester votre code sur le chemin de la base de données. Ces problèmes seront trouvés dans les tests d'intégration. J'ai utilisé cette N-Couches technique pour un certain temps maintenant, et n'ont trouvé aucun problème avec cela.

Les Tests D'Intégration

Évidemment, le test de votre application sur la base de données elle-même est un exercice coûteux et une fois que vous obtenez des dizaines de milliers de tests, il devient un cauchemar, d'autre part, il imite mieux la façon dont le code sera utilisé dans le "monde réel". Ces tests sont importants (de l'interface utilisateur pour la base de données) aussi, et ils seront réalisées dans le cadre des tests d'intégration, PAS de tests unitaires.

Acaz, ce que vous avez vraiment besoin dans votre scénario est un référentiel qui est mockable/fakeable. Si vous souhaitez tester vos contrôleurs comme vous le faites alors votre contrôleur doit être prise dans un objet qui encapsule les fonctionnalités de base de données. Ensuite, il peut retourner tout ce dont vous avez besoin afin de tester tous les aspects de votre fonctionnalité de contrôleur.

voir http://msdn.microsoft.com/en-us/library/ff714955.aspx

Afin de tester le référentiel lui-même (débattue si besoin est, dans tous les cas), vous voulez soit faux, le contexte ou utiliser quelque chose le long des lignes de "Taupes" cadre de référence.

LINQ est par nature difficile à tester. Le fait que la requête est définie en dehors du contexte à l'aide de méthodes d'extension nous donne une grande flexibilité, mais crée un test de cauchemar. Enveloppez votre contexte dans un référentiel et ce problème disparaît.

désolé si longtemps :)

23voto

Soe Moe Points 1650

Comme Ladislav Mrnka mentionné, vous devez tester Linq-to-Entité, mais pas de Linq-to-Objet. Je l'ai utilisé normalement Sql CE que l'essai DB et toujours recréer la base de données avant chaque test. Cela peut faire des test un peu lent, mais jusqu'à présent, je suis OK avec la performance de mon 100+ tests unitaires.

Tout d'abord, modifiez la chaîne de connexion paramètre avec SqlCe dans l' Application.config de vous projet de test.

<connectionStrings>
    <add name="MyDbContext"
       connectionString="Data Source=|DataDirectory|MyDb.sdf"
         providerName="System.Data.SqlServerCe.4.0"
         />
</connectionStrings>

Deuxièmement, définir la db initialiseur avec DropCreateDatabaseAlways.

Database.SetInitializer<MyDbContext>(new DropCreateDatabaseAlways<MyDbContext>());

Et Puis, à force de EF pour initialiser avant l'exécution de chaque test.

public void Setup() {
    Database.SetInitializer<MyDbContext>(new DropCreateDatabaseAlways<MyDbContext>());

    context = new MyDbContext();
    context.Database.Initialize(force: true);
}

Si vous utilisez xunit, Configuration de l'appel de méthode dans votre constructeur. Si vous êtes à l'aide de MSTest, mettre TestInitializeAttribute sur cette méthode. Si nunit.......

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X