Pages

Thursday, December 24, 2015

An Exploration of TDD Part 2 - A Data Repository for the Location Class

Now that I've got my data context class set up, I need a repository for at least basic CRUD operations for my Location class. In order to test this, I create  a new test project. In the UnitTestApp.xaml.cs file I add the following code to the constructor:

using (var db = new QuesterContext())
{
db.Database.Migrate();
}
view raw Migration hosted with ❤ by GitHub
This is to apply any pending migrations before running any of the tests.

Next, the beginning of my LocationRepository class:

public class LocationRepository
{
public IEnumerable<Location> Get()
{
return null;
}
}
This simply returns null to give me my first red test.

In my first test, I'll try to read from an empty table, so I have to clear all entries from it first. After all, I can't be sure that this test won't run after one that adds something to my Locations table.

[TestMethod]
public void Get_returns_an_empty_set_if_table_contains_no_entries()
{
using (QuesterContext dbcontext = new QuesterContext())
{
// Clear the Locations table
dbcontext.RemoveRange(dbcontext.Locations);
dbcontext.SaveChanges();
}
// Now test that Get() returns an empty set
LocationRepository locationRepository = new LocationRepository();
var locations = locationRepository.Get().ToList();
Assert.AreEqual(0, locations.Count);
}
Running the test gives me a red result, as I expected, so now I add the functionality I want to my Get() method:

public class LocationRepository
{
public IEnumerable<Location> Get()
{
IEnumerable<Location> result = null;
using (QuesterContext context = new QuesterContext())
{
result = from e in context.Locations
select e;
}
return result;
}
}
I run my test again and... Gadzooks! It's still red! Looking at the test results, I see the following message: "System.ObjectDisposedException: Cannot access a disposed object."

Well, that's because I forgot that my LINQ query actually uses lazy evaluation, and by the time I get around to trying to use my result (in my test), my database context has already disposed of. So I do a quick fix on my Get() method to force the evaluation of my query while it's still in scope:

public class LocationRepository
{
public IEnumerable<Location> Get()
{
IEnumerable<Location> result = null;
using (QuesterContext context = new QuesterContext())
{
result = (from e in context.Locations
select e).ToList();
}
return result;
}
}
...and running this give me the first green test for my repository class. So there it is - the test did its job to point out my temporary lapse of memory. Good test. Have a biscuit.

Now let's see how we fare with a table that's not empty:

[TestMethod]
public void Get_returns_all_stored_locations()
{
// Create some locations
Location l1 = new Location(4);
Location l2 = new Location(2);
Location l3 = new Location(7);
// Set up the database context
using (QuesterContext dbcontext = new QuesterContext())
{
// Clear the Locations table
dbcontext.RemoveRange(dbcontext.Locations);
// Add the locations I prepared earlier
dbcontext.Locations.Add(l1);
dbcontext.Locations.Add(l2);
dbcontext.Locations.Add(l3);
// Save my changes to the locations table
dbcontext.SaveChanges();
}
// Now test that Get() retrieves entries from the database correctly
LocationRepository locationRepository = new LocationRepository();
var locations = locationRepository.Get().ToList();
// Check that we have the correct number of locations
Assert.AreEqual(3, locations.Count);
}
This test returns green straight away. I have no idea how I could make it red. I guess I should have created it at the same time as the first test, since it tests the same method. Ah, well, live and learn...

P.S. Remember: this is NOT a tutorial on how to write good code. This is an exploration of how useful TDD is in weeding out mistakes, bad code and possibly bad architecture. So I'm throwing everything at it, including the kitchen sink.

P.P.S. You may be able to see where this is going. I'm currently looking at the code I will be using over the next few posts, and I'm not certain if the wetness trickling down my face is tears or if my eyes are bleeding.

P.P.P.S But hey - "For Science!"

Wednesday, December 09, 2015

An Slight Detour From TDD in Favour of Entity Framework 7

Now that I've created a class for my locations, I need a way to store them. Unfortunately Microsoft, for reasons I have yet to fathom, decided that the best way to store data for MS Store applications would be as XML or JSON text files.

Looking at my fledgling project I can already see that there's a good chance that I'll need objects that will contain other objects, all of which need to be referenced to each other - in short, a classic database scenario. And while other platforms have embraced the lovely lightweight database framework that is SQLite... Microsoft hasn't. Quite.

Looking at Nuget I can see more SQLite .NET wrappers than I can shake a stick at, which makes it very hard to decide which one to go with that will be around for the long term. However, there's another possibility. Remember that I said 'Quite' at the end of the previous paragraph? That's because Microsoft is going to offer SQLite support as part of Entity Framework 7 - but that is currently only available as a pre-release. Still, it seems like the best bet for now, and since I'm curious about it anyway, I'll take that route.

To get started, I'll be perusing the (sparse) EF7 documentation at ef.readthedocs.org/en/latest/index.html, but I'll do a quick recap here, for your enjoyment. I'm working with an Universal Windows project, so I
  • Add the following application specific runtime directive to my Default.rd.xml file (found under the project properties): <Type Name="System.Collections.ArrayList" Dynamic="Required All" />. The documentation says that this will become unnecessary at a later point, but for now, in it goes.
  • Install the NuGet packages for EntityFramework.SQLite and EntityFramework.Commands. both are pre-release, so make sure you use the -pre option or tick the box that allows you to search pre-release packages.
With the preliminaries out of the way I use EF7 to create my database context class:

public class QuesterContext : DbContext
{
public DbSet<Location> Locations { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlite(@"Filename=Quester.db");
}
}
view raw WorldContext hosted with ❤ by GitHub
I build my solution and use migrations to create my database by running Add-Migration LocationMigration in the package manager console to create my initial database. This actually required a bit of guesswork because my Location class is in a class library rather than my main app project, but after adding a reference to the library to my main app, the command worked fine.

Now, I can't see much to test in my  class itself, but I do want to test that I can do at least basic CRUD operations with my new data context. I don't think those tests can be classified as unit tests, but hey, they're going to be useful, so I'm gonna add them and worry about the semantics another time.

Coming up: Testing CRUD operations