DbTest 1.0.0
See the version list below for details.
dotnet add package DbTest --version 1.0.0
NuGet\Install-Package DbTest -Version 1.0.0
<PackageReference Include="DbTest" Version="1.0.0" />
<PackageVersion Include="DbTest" Version="1.0.0" />
<PackageReference Include="DbTest" />
paket add DbTest --version 1.0.0
#r "nuget: DbTest, 1.0.0"
#:package DbTest@1.0.0
#addin nuget:?package=DbTest&version=1.0.0
#tool nuget:?package=DbTest&version=1.0.0
DbTest
DbTest is a tiny test library for .NET application. It helps you to write tests with a real database very easy and native. It does only dirty work with the database, and you still need NUnit, XUnit or any of your favorite test framework.
DbTest gives you clean database for each test. It offers easy and maintainable way to create initial fixtures and test cases.
Motivation
Common approach to test .NET application that work with database is to separate data access code from business logic code. This Data Access Layer will be substitute by mocks in tests. It is very power and flexible method, but it also has some disadvantages: producing many boilerplate types, complexity growth and so on. You have to separate business logic and data access even you don't want it. And the saddest thing you still need to test Data Access Layer.
There is a simpler approach which widespead in dynamic language world. Instead of create and control database abstraction, it offers use real database and control it's state. Test framework gives you clean database before each tests and you can create test case by filling it. This approach is significantly more convenient and understandable. Tests with real database are simpler and give more confidence.
Influences
DbTest is inspired by the test approach from Ruby on Rails, Django, Yii2 and many other perfect dynamic language frameworks. This approach is a very popular in dynamic languages.
What's you get
Clean real database Real database has many aspects that can not be emulated by Data Access Layer: constraints, triggers, complex SQL queries and so on. DbTest reset database to initial state before each test. SqlServer and Postgresql are supported now.
Strong type fixtures To define initial state you can use fixtures in txt files. Any changes in project can broke those fixtures, because compiler don't process them. DbTest offer a way to describe fixtures in code. You will get autocomplete, refactoring and type checking when you write and maintain fixtures.
Idea to make test case The main idea to create high level builder that fill tables in database. Each method in this builder create one business model. See more details in Concepts
Installation
Install in your *.Tests
project:
Install-Package DbTest
You can see usages in Examples
Prepare database
You need some database to tests. For little project sqlite
could be a good choice (as a compromise between performance and reliability of tests). For complex scenarious you need same database which you use in production. As usual it is not problem:
Mysql, Postgresql - lite enough, MSSQL - has LocalDb mode.
In now days DbTest work only with MSSQL out-of-box, but it can configurate to work with any database throw IDatabasePrepare interface. DbTest has methods to reset database and load initial fixtures:
var dbTest = new TestDatabase(dataLayer);
dbTest.ResetWithFixtures(
new Countries(),
new Manufacturers(),
new Products()
);
Fixtures
A real system has many relations between models, we need to fill many tables before create one row in a target table. For example, Products can be related to Manufacturers which related to Country.
Let's start from model which has no relations:
public class Countries : IModelFixture<Country>
{
public string TableName => "Countries";
public static Country Scotland => new Country
{
Id = 1,
Name = "Scotland",
IsDeleted = false
};
public static Country USA => new Country
{
Id = 2,
Name = "USA",
IsDeleted = false
};
}
First of all we need class that realize IModelFixture<T>
interface. Each model instances are declared as static to give easy access to it from any part of other fixtures or tests. You need set identifiers explicitly and control uniqueness between one class of model.
Now, we are ready to create Manufacturers and Products.
class Manufacturers : IModelFixture<Manufacturer>
{
public string TableName => "Manufacturers";
public static Manufacturer BrownForman => new Manufacturer
{
Id = 1,
Name = "Brown-Forman",
CountryId = Countries.USA.Id,
IsDeleted = false
};
public static Manufacturer TheEdringtonGroup => new Manufacturer
{
Id = 2,
Name = "The Edrington Group",
CountryId = Countries.Scotland.Id,
IsDeleted = false
};
}
public class Goods : IModelFixture<Good>
{
public string TableName => "Goods";
public static Good JackDaniels => new Good
{
Id = 1,
Name = "Jack Daniels, 0.5l",
ManufacturerId = Manufacturers.BrownForman.Id,
IsDeleted = false
};
public static Good FamousGrouseFinest => new Good
{
Id = 2,
Name = "The Famous Grouse Finest, 0.5l",
ManufacturerId = Manufacturers.TheEdringtonGroup.Id,
IsDeleted = false
};
}
Pay attention to external keys in models we do not set it explicitly instead take value from another fixture. You give advise from intellisence when create your fixtures and check from compiler when your system change and fixtures must changed too.
World
World
is a conception of test environment. First of all, it is a point to prepare database - clean, load initial fixtures.
Secondly, you can create singletons, set values to static variable and so on. For example, for asp.net mvc
you can set HttpContext
with a user:
static class World
{
public static void InitDatabase()
{
using (var context = new MyContext())
{
var dbTest = new EFTestDatabase<MyContext>(context);
dbTest.ResetWithFixtures(
new Countries(),
new Manufacturers(),
new Goods()
);
}
}
public static void InitContextWithUser()
{
HttpContext.Current = new HttpContext(
new HttpRequest("", "http://your-domain.com", ""),
new HttpResponse(new StringWriter())
);
HttpContext.Current.User = new GenericPrincipal(
new GenericIdentity("root"),
new string[0]
);
}
}
Model builder
Model builder
is a helper to create test case. It is not included in DbTest
, you have to create it. Each methods of this class must
create and return one entity:
public class ModelBuilder
{
public MoveDocument CreateDocument(string time, Storage source, Storage dest)
{
var document = new MoveDocument
{
Number = "#",
SourceStorageId = source.Id,
DestStorageId = dest.Id,
Time = ParseTime(time),
IsDeleted = false
};
using (var db = new MyContext())
{
db.MoveDocuments.Add(document);
db.SaveChanges();
}
return document;
}
public MoveDocumentItem AddGood(MoveDocument document, Good good, decimal count)
{
var item = new MoveDocumentItem
{
MoveDocumentId = document.Id,
GoodId = good.Id,
Count = count
};
using (var db = new MyContext())
{
db.MoveDocumentItems.Add(item);
db.SaveChanges();
}
return item;
}
}
Use in test
[SetUp]
public void SetUp()
{
World.InitDatabase(); // Main part, prepare database for test
}
[Test]
public void CalculateRemainsForMoveDocuments()
{
// prepare test
var builder = new ModelBuilder();
var doc1 = builder.CreateDocument("15.01.2016 10:00:00", Storages.MainStorage, Storages.RemoteStorage);
builder.AddGood(doc1, Goods.JackDaniels, 10);
builder.AddGood(doc1, Goods.FamousGrouseFinest, 15);
var doc2 = builder.CreateDocument("16.01.2016 20:00:00", Storages.RemoteStorage, Storages.MainStorage);
builder.AddGood(doc2, Goods.FamousGrouseFinest, 7);
// test
var remains = RemainsService.GetRemainFor(Storages.RemoteStorage, new DateTime(2016, 02, 01));
// assert
Assert.AreEqual(2, remains.Count);
Assert.AreEqual(10, remains.Single(x => x.GoodId == Goods.JackDaniels.Id).Count);
Assert.AreEqual(8, remains.Single(x => x.GoodId == Goods.FamousGrouseFinest.Id).Count);
}
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- Microsoft.EntityFrameworkCore (>= 8.0.10)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.10)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on DbTest:
Package | Downloads |
---|---|
DbTest.EFCore
Extension for DbTest to use with Entity Framework Core |
|
DbTest.EF6
Extension for DbTest to use with Entity Framework 6 |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last Updated |
---|---|---|
1.1.4 | 177 | 10/16/2024 |
1.1.3 | 135 | 10/15/2024 |
1.1.2 | 131 | 10/15/2024 |
1.1.1 | 137 | 10/15/2024 |
1.1.0 | 127 | 10/15/2024 |
1.0.0 | 143 | 10/15/2024 |
0.2.0 | 1,956 | 3/8/2018 |
0.1.8 | 1,451 | 5/22/2017 |
0.1.7 | 1,246 | 5/22/2017 |
0.1.6 | 1,247 | 5/22/2017 |
0.1.5 | 1,298 | 1/10/2017 |
0.1.4 | 1,497 | 1/10/2017 |
0.1.3 | 1,364 | 1/2/2017 |
0.1.2 | 1,338 | 1/2/2017 |
0.1.1 | 1,361 | 1/2/2017 |
0.1.0 | 1,354 | 1/2/2017 |