Ich weiß, dass es einige ähnliche Fragen gibt.
Aber ich glaube nicht, dass beide der obigen Frage eine klare Antwort haben, die meiner Anforderung entspricht.
Im Moment entwickle ich ein neues WebAPI-Projekt und spalte zwischen WebAPI-Projekt und DataAccess-Technologie. Ich habe kein Problem, den Controller für WebAPI zu testen, da ich die Datenzugriffsklasse verspotten kann.
Aber für DataAccess-Klasse, die eine andere Geschichte ist, da ich Dapper mit Inline-Abfragen darin verwende, bin ich ein bisschen verwirrt, wie ich es mit Unit Test testen kann. Ich habe einige meiner Freunde gefragt und sie bevorzugen es, den Integrationstest anstelle des Komponententests durchzuführen.
Was ich wissen möchte, ist, dass es möglich ist, die DataAccess-Klasse, die Dapper- und Inline-Abfragen verwendet, in einem Unit-Test zu testen.
Nehmen wir an, ich habe eine Klasse wie diese (dies ist eine generische Repository-Klasse, da viele der Codes ähnliche Abfragen haben, die sich nach Tabellenname und Feld unterscheiden)
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
}
Und die Implementierung für obigen Code ist wie
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
}
Hier ist unser Ansatz:
Zuallererst müssen Sie eine Abstraktion über IDbConnection
, um es zu verspotten:
public interface IDatabaseConnectionFactory
{
IDbConnection GetConnection();
}
Ihr Repository würde die Verbindung von dieser Factory erhalten und die Dapper
Abfrage ausführen:
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");
}
}
Ihr Test würde eine In-Memory-Datenbank mit einigen Beispielzeilen erstellen und prüfen, wie das Repository sie abruft:
[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);
}
Ich denke, es gibt mehrere Möglichkeiten, eine solche In-Memory-Datenbank zu implementieren. Wir haben OrmLite
auf der SQLite
Datenbank verwendet:
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);
}
}
}
}