8
votes

Test de l'unité Une couche d'accès aux données

J'ai lu sur l'unité testant la couche d'accès aux données d'un projet. La plupart des options à ébullition jusqu'à:

  • Utilisez une base de données de test dédiée, mais nettoyez-la dans la phase de finalisation de tous les tests de l'unité (ou faites-le manuellement)
  • Utilisez la base de données mais ne vous engagez pas ou ne roule pas simplement
  • Mock La base de données

    Dans un ancien projet, nous utilisions la voie à la restauration, mais j'aime en savoir plus sur les autres options et comment elles sont mieux effectuées. Si vous avez des échantillons / des articles / vidéos / ... veuillez les partager.


0 commentaires

4 Réponses :


2
votes

Je préfère utiliser une base de données de test au lieu de Ne pas commettre idée.

Ma base de données de développement contient des enregistrements factices ou un échantillonnage entièrement désinfecté de données de production.

Ma base de données d'essai d'intégration est une copie de la base de données de production réelle (cette version est celle utilisée pour tester juste avant que je ne change en direct).


0 commentaires

4
votes

Vous devez avoir 2 types de tests avec un projet. Tests d'unité et tests d'intégration

Les tests unitaires testent un aspect de votre projet sans dépendances sur l'accès et la présentation des données. Pour les tests d'unités, vous vous moqueriez de votre base de données et de votre injection de dépendance de l'utilisateur pour découpler votre code à partir du fournisseur de données. Cela conduit à une meilleure architecture et vous permet de brancher un fournisseur de données différent si vous le souhaitez. par exemple. passer d'ado.net à NHibernate.

Les tests d'intégration sont l'endroit où vous testez votre système entier et assurez-vous que votre code peut obtenir les données correctes de la base de données, etc. pour les tests d'intégration que chaque développeur doit avoir une copie de la base de données sur laquelle ils peuvent tester. Vous devriez essayer d'automatiser la création et la population de votre base de données afin de pouvoir revenir rapidement et facilement à une bonne copie de la base de données. Outils tels que Nant et DBFIT vous aidera à scripter de votre création de bases de données.

Je n'utiliserais pas une base de données centrale pour tester car d'autres développeurs peuvent être testés au même moment et cela pourrait ne pas être dans un bon état et que vous pourriez obtenir de faux positifs et passer des âges à essayer de déboguer un problème qui n'est pas un problème.


0 commentaires

0
votes

Je me moquerais de la base de données. Traiter avec la base de données dans un test est douloureux, car vous devez créer la base de données, créer un schéma, puis le laisser tomber, assurez-vous qu'il n'y a pas de connexions suspendues, etc. est douloureuse.

Une autre chose qui me met mal à l'aise est le fait que vérifie Votre logique de code est «trop loin» du code. Je descendrais sur la route de la mise en place de fonctions SQL (connexions, commandes, etc.) derrière une classe moquible et vérifiez que DAL appelle les bonnes méthodes. En outre, les tests courent beaucoup plus rapidement de cette façon. P>

Voici quelques classes d'abstraction SQL rapides et exemple d'utilisation + test unitaire. P>

public class SqlConnectionBase : IDisposable {
    private readonly SqlConnection m_Connection;

    public SqlConnectionBase(string connString) {
        m_Connection = new SqlConnection(connString);
    }

    public virtual SqlConnection Object { get { return m_Connection; } }

    public virtual void Open() {
        m_Connection.Open();
    }
    public virtual void Close() {
        m_Connection.Close();
    }

    #region IDisposable Members

    public virtual void Dispose() {
        m_Connection.Dispose();
    }

    #endregion
}

public class SqlCommandBase  : IDisposable{
    private readonly SqlCommand m_Command;

    public SqlCommandBase() {
        m_Command = new SqlCommand();
    }
    public SqlCommandBase(string cmdText, SqlConnectionBase connection) {
        m_Command = new SqlCommand(cmdText, connection.Object);
    }
    public SqlCommandBase(SqlConnectionBase connection) {
        m_Command = new SqlCommand();
        m_Command.Connection = connection.Object;
    }

    public virtual int ExecuteNonQuery() { return m_Command.ExecuteNonQuery(); }
    public virtual string CommandText { get { return m_Command.CommandText; } set { m_Command.CommandText = value; } }

    public virtual void AddParameter(SqlParameter sqlParameter) {
        m_Command.Parameters.Add(sqlParameter);
    }

    #region IDisposable Members

    virtual public void Dispose() {
        m_Command.Dispose();
    }

    #endregion
}
public class SqlFactory {
    public virtual SqlCommandBase CreateCommand(string query, SqlConnectionBase conn) {
        return new SqlCommandBase(query, conn);
    }
    public virtual SqlCommandBase CreateCommand(SqlConnectionBase conn) {
        return new SqlCommandBase(conn);
    }

    public virtual SqlConnectionBase CreateConnection(string connString) {
        return new SqlConnectionBase(connString);
    }

}

public class DBUser {
   public DBUser(SqlFactory factory) {
     m_factory = factory; //dependency constructor, will be used during unit testing
   }

   public DBUser() {
     m_factory = new SqlFactory(); //used during normal execution
   }

   public void DoSomething() {
     var conn = m_factory.CreateConnection("server=servername,database=...");
     var cmd =  m_factory.CreateCommand(conn);
     cmd.CommandText = "Select * from users";
     cmd.ExecuteNonQuery();
   }

   [TestMethod]
   public void DoSomethingTest() {
     var factoryMock = new Mock<SqlFactory>();
     var cmdMock = new Mock<CommandBase>();
     factoryMock.Setup(f=>f.CreateConnection(It.IsAny<string>())).Returns(cmdMock.Object);
     DBUser dbUser = new DBUser(factoryMock.Object);
     dbUser.DoSomething();

     //Verify that DoSomething is called.
     cmdMock.Verify(c=>c.DoSomething());
   }
}


2 commentaires

Les tests courent beaucoup plus vite de cette façon: je peux imaginer. Avez-vous un exemple de code de ce que vous avez décrit?


J'ai des morceaux et des morceaux, j'envisage sérieusement l'écriture system.sql.abstractions la bibliothèque, similaire à System.IO.Abstractions



1
votes

La principale responsabilité de DAL est de persister / récupérer des données à partir de la base de données, le système sous test est donc DAL +. Il n'y a pas de sens pour écrire des tests sur DAL à l'aide de la base de données simulée - qui se soucie vraiment de ce que la requête SQL a été exécutée pour aller chercher une entité particulière? Il est nécessaire de vérifier si une entité juste a été sélectionnée et que tous les attributs ont été correctement mappés.

Pour ce faire, je nettoie habituellement la base de données, remplissez la base de données avec des données de test et récupérez-la avec des méthodes DAL. P>

       [SetUp]
    public void SetUp()
    {
        Database.Clear();
        manager = new ServiceManager();
    }

    [TearDown]
    public void TearDown()
    {
        manager.Dispose();
    }

    [Test]
    public void InsertAndLoadOrderContract()
    {
        MinOrderModel model = new OrderBuilder().InsertMinimalOrder(manager);

        Contract contract = TestObjectGenerator.GenerateEntity<Contract>();
        manager.Contract.InsertOrderContract(model.Order.OrderCompositeId, contract);

        Contract selectedContract = manager.Contract.SelectById(contract.ContractId);

        AssertContract(selectedContract, contract);
    }

    private static void AssertContract(IContract actual, IContract expected)
    {
        Assert.That(actual.AgencyCodeOther, Is.EqualTo(expected.AgencyCodeOther));
        Assert.That(actual.AgencyFK, Is.EqualTo(expected.AgencyFK));
        Assert.That(actual.ContractId, Is.EqualTo(expected.ContractId));
        Assert.That(actual.Ident, Is.EqualTo(expected.Ident));
    }


0 commentaires