2010-12-07 81 views
2

Dans le passé, j'ai des tests unitaires écrits pour les opérations CRUD simples lors de la création d'accès aux données/code référentiel qui ressemblent quelque chose comme ceci:code EF4 En premier lieu, TDD, CRUD et Transactions

using(var connection = new WhateverConnection(connectionString)) 
{ 
    connection.Open(); 
    using(var transaction = connection.BeginTransaction()) 
    { 
     try 
     { 
      //test the CRUD operation 
     } 
     finally 
     { 
      //gets rid of any stuff created during the test 
      transaction.Rollback(); 
     } 
    } 
} 

je déconner autour avec EF4 Code First aujourd'hui, et j'ai réalisé que je ne sais pas comment ce scénario de test se traduit dans le lexique Entity Framework. Il semble que, si j'appelle DbContext.SaveChanges(), il enregistre les validations et, même si AcceptAllChanges() a été appelée ou non. Même en utilisant ObjectContext au lieu de DbContext, je ne peux pas comprendre comment ce scénario de test simple peut être recréé sans nettoyer manuellement tous les objets de simulation/test créés. J'ai lu this article on MSDN, mais TransactionScope n'a pas vraiment de méthode de type Rollback non plus. Est-ce que j'utilise TransactionScope et n'appelle jamais Complete? Existe-t-il une autre méthode ou manière d'utiliser DbContext et/ou ObjectContext pour effectuer un Rollback lors de tests unitaires? Ai-je besoin de complètement réajuster ma pensée pour TDD avec EF4 Code First?

Répondre

8

ObjectContext lui-même n'expose pas le comportement transactionnel. Vous devez emballer le code EF dans la transaction par vous-même. La façon la plus simple de le faire est d'utiliser TransactionScope. Si vous n'appelez pas la méthode Complete sur la portée et que vous l'éliminez, elle effectuera une restauration. Nous utilisons généralement la classe de base pour ce type de test d'intégration:

[TestClass] 
public abstract class TransactionTestBase 
{ 
    private TransactionScope scope = null; 

    [TestInitialize] 
    public virtual void TestInitialize() 
    { 
     scope = new TransactionScope(TransactionScopeOption.RequiresNew, 
      new TransactionOptions() 
       { 
        IsolationLevel = IsolationLevel.ReadUncommitted 
       }); 
    } 

    [TestCleanup] 
    public virtual void TestCleanup() 
    { 
     if (scope != null) 
     { 
      scope.Dispose(); 
      scope = null; 
     } 
    } 
} 

Toutes les classes de test dérivent de cette classe. TestInitialize est appelée avant chaque TestMethod dans la classe dérivée et TestCleanup est appelée après chaque TestMethod afin que vos tests n'aient pas du tout à traiter avec la transaction.

+0

Cela fonctionne. Cependant, je noterai que cela ne fonctionne pas contre Sql Server CE (SqlCe), qui est ce que beaucoup d'exemples de Code First que j'ai trouvé utilisent. En outre, le TransactionScope échouera si vous utilisez un Initializer de type 'RecreateDatabaseIfModelChanges', car DROP DATABASE n'est pas autorisé dans un TransactionScope. –