Unit Testing Dapper avec des requêtes en ligne

c# dapper unit-testing

Question

Je sais qu'il y a plusieurs questions similaires aux miennes.

mais je ne pense pas que les deux questions ci-dessus répondent clairement à mes exigences.

En ce moment, je développe un nouveau projet WebAPI et je le partage entre le projet WebAPI et la technologie DataAccess. Je n'ai pas de problème à tester le contrôleur pour WebAPI car je peux simuler la classe d'accès aux données.

Mais pour la classe DataAccess qui est une histoire différente, puisque j'utilise Dapper avec des requêtes en ligne, je suis un peu perplexe sur la façon de la tester en utilisant Unit Test. J'ai demandé à certains de mes amis et ils préfèrent faire le test d'intégration au lieu du test unitaire.

Ce que je veux savoir, c'est qu'il est possible de tester la classe DataAccess qui utilise les requêtes Dapper et Inline.

Disons que j'ai une classe comme celle-ci (il s'agit d'une classe de référentiel générique, car beaucoup de codes ont des requêtes similaires différenciées par nom de table et champ)

public abstract class Repository<T> : SyncTwoWayXI, IRepository<T> where T : IDatabaseTable
{
       public virtual IResult<T> GetItem(String accountName, long id)
       {
            if (id <= 0) return null;

            SqlBuilder builder = new SqlBuilder();
            var query = builder.AddTemplate("SELECT /**select**/ /**from**/ /**where**/");

            builder.Select(string.Join(",", typeof(T).GetProperties().Where(p => p.CustomAttributes.All(a => a.AttributeType != typeof(SqlMapperExtensions.DapperIgnore))).Select(p => p.Name)));
            builder.From(typeof(T).Name);
            builder.Where("id = @id", new { id });
            builder.Where("accountID = @accountID", new { accountID = accountName });
            builder.Where("state != 'DELETED'");

            var result = new Result<T>();
            var queryResult = sqlConn.Query<T>(query.RawSql, query.Parameters);

            if (queryResult == null || !queryResult.Any())
            {
                result.Message = "No Data Found";
                return result;
            }

            result = new Result<T>(queryResult.ElementAt(0));
            return result;
       }

       // Code for Create, Update and Delete
  }

Et l'implémentation pour le code ci-dessus est comme

public class ProductIndex: IDatabaseTable
{
        [SqlMapperExtensions.DapperKey]
        public Int64 id { get; set; }

        public string accountID { get; set; }
        public string userID { get; set; }
        public string deviceID { get; set; }
        public string deviceName { get; set; }
        public Int64 transactionID { get; set; }
        public string state { get; set; }
        public DateTime lastUpdated { get; set; }
        public string code { get; set; }
        public string description { get; set; }
        public float rate { get; set; }
        public string taxable { get; set; }
        public float cost { get; set; }
        public string category { get; set; }
        public int? type { get; set; }
}

public class ProductsRepository : Repository<ProductIndex>
{
   // ..override Create, Update, Delete method
}

Réponse populaire

Voici notre approche:

  1. Tout d'abord, vous devez avoir une abstraction par-dessus IDbConnection pour pouvoir la IDbConnection :

    public interface IDatabaseConnectionFactory
    {
        IDbConnection GetConnection();
    }
    
  2. Votre référentiel obtiendrait la connexion de cette fabrique et exécutera la requête Dapper :

    public class ProductRepository
    {
        private readonly IDatabaseConnectionFactory connectionFactory;
    
        public ProductRepository(IDatabaseConnectionFactory connectionFactory)
        {
            this.connectionFactory = connectionFactory;
        }
    
        public Task<IEnumerable<Product>> GetAll()
        {
            return this.connectionFactory.GetConnection().QueryAsync<Product>(
                "select * from Product");
        }
    }
    
  3. Votre test créera une base de données en mémoire avec des exemples de lignes et vérifiera comment le référentiel les récupère:

    [Test]
    public async Task QueryTest()
    {
        // Arrange
        var products = new List<Product>
        {
            new Product { ... },
            new Product { ... }
        };
        var db = new InMemoryDatabase();
        db.Insert(products);
        connectionFactoryMock.Setup(c => c.GetConnection()).Returns(db.OpenConnection());
    
        // Act
        var result = await new ProductRepository(connectionFactoryMock.Object).GetAll();
    
        // Assert
        result.ShouldBeEquivalentTo(products);
    }
    
  4. Je suppose qu'il existe plusieurs façons d'implémenter une telle base de données en mémoire; nous avons utilisé OrmLite au dessus de la SQLite données SQLite :

    public class InMemoryDatabase
    {
        private readonly OrmLiteConnectionFactory dbFactory = new OrmLiteConnectionFactory(":memory:", SqliteOrmLiteDialectProvider.Instance);
    
        public IDbConnection OpenConnection() => this.dbFactory.OpenDbConnection();
    
        public void Insert<T>(IEnumerable<T> items)
        {
            using (var db = this.OpenConnection())
            {
                db.CreateTableIfNotExists<T>();
                foreach (var item in items)
                {
                    db.Insert(item);
                }
            }
        }
    }
    


Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Est-ce KB légal? Oui, apprenez pourquoi