Skip to content

timostamm/Datafixtures

Repository files navigation

Datafixtures

.NET Datafixtures for Unit Tests

Test fixtures can be used to setup a certain environment state - usually a database. They make unit testing easier because you can easily enforce a particular state neccessary to test your code. Fixtures should be able to depend on each other, so that each fixture can focus on its own data.

This library provides a Fixture base class and a fixture loader that takes care of dependencies.

Defining and loading simple fixtures

A simple fixture extends Fixture<T> and overrides DoLoad():

class StartOfUnixTime : Fixture<DateTime>
{
   protected override DateTime DoLoad()
    {
        return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    }
}

To use this fixture, you need to load it:

var loader = new FixtureLoader();
var unixStart = loader.Add<StartOfUnixTime>();
loader.Load();

// The data generated by the fixture is available via the property "Result".
Assert.AreEqual(1970, unixStart.Result.Year);

To make a fixture depend on another fixture, simply reference it as a constructor argument.

class UnixStartMessage : Fixture<string>
{

    private readonly StartOfUnixTime start;

    public UnixStartMessage(StartOfUnixTime start)
    {
        this.start = start;
    }

    protected override string DoLoad()
    {
        // You can access the result of the StartOfUnixTime 
        // fixture via its Result property
        return "hello world, it is " + start.Result.Year;
    }
}

When loading this fixture, the dependant fixtures are loaded automatically. You just need to load the fixture you are actually using.

var loader = new FixtureLoader();
var msg = loader.Add<UnixStartMessage>();
loader.Load();
Assert.AreEqual("hello world, it is 1970", msg.Result);
Using fixtures with the EntityFramework

In a fixture, you probably want to add an entity to your db context and save the changes. The db context is just another dependency and can simply be injected as a constructor argument.

class FirstStudent : Fixture<Student>
{

    private readonly SchoolDbContext schoolDb;
    
    public UnixStartMessage(SchoolDbContext schoolDb)
    {
        this.schoolDb = schoolDb;
    }

    protected override Student DoLoad()
    {
        var first = new Student()
        {
            StudentName = "Donald Knuth" 
        };
        schoolDb.Students.AddObject( first );
        return first;
    }
}
Clearing and initializing the database

Loading Database fixtures has a few implications. If you want your database to be in a repeatable state, you have to empty it first. You can use the utility class EntityFixtures to drop the entire database, initialize it, and load your fixtures:

var schoolDb = new SchoolDbContext();
FixtureLoader loader = EntityFixtures.DropCreateDatabase(scoolDb)
var first = loader.add<FirstStudent>();
loader.load();

// now you have a clean database with the first student
System.Diagnostics.Trace.WriteLine("First student: " + first.Result.StudentName); // => "First student: Donald Knuth"
Drop the database ?!?

You should use a LocalDB for your unittests. EntityFixtures.DropCreateDatabase() actually throws an exception if you do not, but in any case: use your own brain.

Injecting other dependencies

If you have a look at EntityFixtures.DropCreateDatabase(), it just calls FixtureLoader.RegisterService() with your DbContext, and thereby making it available for constructor injection:

loader.RegisterService<TContext>(context);

You are of course free to register and use other dependencies.

About

.NET Datafixtures for Unit Tests

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages