EF Code First and WPF with the Chinook database. Part 1c – the unit tests

In the final part of the server side code (we have already written a Code First provider and data provider) I am going to implement some unit tests for the two methods I currently have on the data provider.

My unit tests will need to have no access to the database as that would break the encapsulation of the test so we need to mock out the raw data access layer while still providing the functionality that we would expect from the Entity Framework context.

To do this we need to make several changes to the current project to allow us to inject a mocked up context into the data provider.

The first thing we need to do is to abstract the context used by the data provider out into an interface, being as we currently have a small set of classes then the interface is simply:

public interface IContext
{
    IDbSet<Artist> Artist { get; set; }

    IDbSet<Album> Album { get; set; }

    IDbSet<Track> Track { get; set; }
}

Notice the IDbSet<T> – this is to allow us to mock up the DbSet’s using the same interface.

Next up we need to edit our Chinook class so that it uses this new interface (note, again, the IDbSet<T>’s)

public class Chinook : DbContext, IContext
{
    protected override void OnModelCreating(System.Data.Entity.ModelConfiguration.ModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
    }

    public IDbSet<Artist> Artist { get; set; }

    public IDbSet<Album> Album { get; set; }

    public IDbSet<Track> Track { get; set; }
}

Now we have this we can use the interface in the data provider and set it to a Chinook instance by default (for the time being).

private IContext db = new Chinook();

Right we now have a good basis to start working on the test so add a new test project to the solution and create a new class called MockDbSet.  This allows us to simulate the actions of a real DbSet in the database for a given set of objects.  Huge thanks to Julie Lerman for showing the starting point for this work with her series on EF4 repositories (A great read!).

The MockDbSet implements the IDbSet<T> Interface supplied by the Entity Framework and allows us to replicate the functionality as follows (note I have not implemented the create or find methods as I don’t need them right now.)

public class MockDbSet<T> : IDbSet<T> where T : class
{
    readonly IList<T> _container = new List<T>();

    public T Add(T entity)
    {
        _container.Add(entity);
        return entity;
    }

    public T Attach(T entity)
    {
        _container.Add(entity);
        return entity;
    }

    public TDerivedEntity Create<TDerivedEntity>() where TDerivedEntity : class, T
    {
        throw new NotImplementedException();
    }

    public T Create()
    {
        throw new NotImplementedException();
    }

    public T Find(params object[] keyValues)
    {
        throw new NotImplementedException();
    }

    public System.Collections.ObjectModel.ObservableCollection<T> Local
    {
        get
        {
            return new ObservableCollection<T>(_container);
        }
    }

    public T Remove(T entity)
    {
        _container.Remove(entity);
        return entity;
    }

    public IEnumerator<T> GetEnumerator()
    {
        return _container.GetEnumerator();
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return _container.GetEnumerator();
    }

    public Type ElementType
    {
        get
        {
            return typeof(T);
        }
    }

    public System.Linq.Expressions.Expression Expression
    {
        get
        {
            return _container.AsQueryable<T>().Expression;
        }
    }

    public IQueryProvider Provider
    {
        get
        {
            return _container.AsQueryable<T>().Provider;
        }
    }
 }

This is a simple implementation that stores the set in a local List.

Now we have a mock set we can mock up the context which is something that implements IContext and allows us to access the DbSets we want to work with outside of a database environment.  I have added some methods to add test data into the sets as well.

class MockContext : IContext
{
    private IDbSet<Artist> artist;
    private IDbSet<Album> album;
    private IDbSet<Track> track;

    public IDbSet<Artist> Artist
    {
        get
        {
            this.CreateArtists();
            return this.artist;
        }

        set
        {
            throw new NotImplementedException();
        }
    }

    public IDbSet<Album> Album
    {
        get
        {
            this.CreateAlbums();
            return this.album;
        }
        
        set
        {
            throw new NotImplementedException();
        }
    }

    public IDbSet<Track> Track
    {
        get
        {
            this.CreateTracks();
            return this.track;
        }
        
        set
        {
            throw new NotImplementedException();
        }
    }

    private void CreateArtists()
    {
        if (artist == null)
        {
            artist = new MockDbSet<Artist>();
            artist.Add(new Artist { ArtistId = 1, Name = "Test Artist 1" });
            artist.Add(new Artist { ArtistId = 2, Name = "Test Artist 2" });
            artist.Add(new Artist { ArtistId = 3, Name = "Test Artist 3" });
        }
    }

    private void CreateAlbums()
    {
        if (album == null)
        {
            album = new MockDbSet<Album>();
            album.Add(new Album { AlbumId = 1, Title = "Test Album 1", ArtistId = 1 });
            album.Add(new Album { AlbumId = 2, Title = "Test Album 2", ArtistId = 2 });
            album.Add(new Album { AlbumId = 3, Title = "Test Album 3", ArtistId = 3 });
            album.Add(new Album { AlbumId = 4, Title = "Test Album 4", ArtistId = 3 });
        }
    }

    private void CreateTracks()
    {
        if (track == null)
        {
            track = new MockDbSet<Track>();
            track.Add(new Track { TrackId = 1, Name = "Test Track 1", AlbumId = 1 });
            track.Add(new Track { TrackId = 2, Name = "Test Track 2", AlbumId = 1 });
            track.Add(new Track { TrackId = 3, Name = "Test Track 3", AlbumId = 1 });
            track.Add(new Track { TrackId = 4, Name = "Test Track 4", AlbumId = 2 });
            track.Add(new Track { TrackId = 5, Name = "Test Track 5", AlbumId = 2 });
            track.Add(new Track { TrackId = 6, Name = "Test Track 6", AlbumId = 2 });
            track.Add(new Track { TrackId = 7, Name = "Test Track 7", AlbumId = 3 });
            track.Add(new Track { TrackId = 8, Name = "Test Track 8", AlbumId = 3 });
            track.Add(new Track { TrackId = 9, Name = "Test Track 9", AlbumId = 4 });
            track.Add(new Track { TrackId = 10, Name = "Test Track 10", AlbumId = 4 });
        }
    }
}

Now we have a context and sets mocked up we can almost write our tests but at the moment we have no way of getting the new mock context into the data provider.  This is where we can use an often overlooked assembly attribute, InternalsVisibleTo, which allows the given project access to internal members of the a project.  So all we need to do is apply the following to the AssemblyInfo.cs class of the model project.

[assembly: InternalsVisibleTo("MusicApp.Model.Tests")]

and change the context member in the data provider (db) from private to internal and we can access it from the our test project.

I have two tests – one for each method in the data provider and have implemented them as follows:

public class EFCodeFirstDataProviderTest
{
    [TestMethod()]
    public void GetArtistsTest()
    {
        EFCodeFirstDataProvider target = new EFCodeFirstDataProvider() { db = new MockContext() };

        List<Artist> expected = new List<Artist>();
        expected.Add(new Artist { ArtistId = 1, Name = "Test Artist 1" });
        expected.Add(new Artist { ArtistId = 2, Name = "Test Artist 2" });
        expected.Add(new Artist { ArtistId = 3, Name = "Test Artist 3" });

        List<Artist> actual = target.GetArtists();
        CollectionAssert.AreEquivalent(expected, actual);
    }

    [TestMethod()]
    public void SearchArtistsTest()
    {
        EFCodeFirstDataProvider target = new EFCodeFirstDataProvider() { db = new MockContext() };

        string searchTerm = "Artist 1";
        List<Artist> expected = new List<Artist>();
        expected.Add(new Artist { ArtistId = 1, Name = "Test Artist 1" });

        List<Artist> actual = target.SearchArtists(searchTerm);

        CollectionAssert.AreEquivalent(expected, actual);
    }
}

As you can see they are simple tests to determine that the methods return the objects that I wanted.  As they stand these methods will fail as the CollectionAssert.AreEquivalent method checks for equality and we have no override for equality (or hash code) in our Artist class so we just need to add them in:

public override int GetHashCode()
{
    return ArtistId;
}

public override bool Equals(object obj)
{
    return ArtistId == (obj as Artist).ArtistId;
}

and the tests will now pass.

In the next post I will create a simple WPF front-end to display the information and then I will build the interface from that point on.

Using JustMock and Dependency Injection to mock data providers

Justmock is an excellent, fairly new, mocking framework from Telerik that I had the pleasure of getting a free licence for at the launch event.  The company I work for has now picked this up and is intending on rolling it out across the development department and I don’t think it can happen soon enough.  This is a conversation I have just had (edited and redacted for this blog) with a colleague about using it to mock up the fetching of data.

The current set up was similar to this much more contrived example but it serves to demonstrate the changes we made.

We had a class that depended on the data provider to access the database and return actual values to verify a method was working.  For this example I am using simple number checking but the actual problem was much larger.  The data provider fetches a list of numbers based on an initial value and then the checker returns the maximum value in the results set.  The data provider interface and checker are below.

public interface IDataProvider
{
    List GetNumbers(int start);
}

public class DataProvider : IDataProvider
{
    public List GetNumbers(int start)
    {
        var list = new List();
        // Use EF to access db and return list based on parameter
        // return a new list for this example
        return list;
    }
}

public class NumberChecker
{
    public int Getmax()
    {
        IDataProvider dp = new DataProvider();
        return dp.GetNumbers(5).Max();
    }

    public List GetNumbers()
    {
        IDataProvider dp = new DataProvider();
        return dp.GetNumbers(5);
    }
}

This is very simple code and the associated test went something like this.

[TestMethod()]
public void CheckCheckGeneratorReturnsMaximumValue()
{
    NumberChecker checker = new NumberChecker();
    int expected = checker.GetNumbers().Max();
    int actual = checker.Getmax();
    Assert.AreEqual(expected, actual);
}

This test passes but its not very nice.  It calls the generator methods twice and then continues to touch the database in order to get the data.

We really wanted to use JustMock so we broke it out and installed it on his machine.

To refactor this code into something testable we first had to remove that dependency on the data provider. The fact that it has an interface makes this much easier as we can simply inject an implementer of this interface into the method and act on that.  So without changing the implementation of the data provider and the interface the NumberChecker class became the following.

public class NumberChecker
{
    public int Getmax(IDataProvider dp)
    {
        return dp.GetNumbers(5).Max();
    }

    public List GetNumbers(IDataProvider dp)
    {
        return dp.GetNumbers(5);
    }
}

Here we have moved the data provider up to a parameter and are now able to inject any implementer we choose into this method.

This now allows us to test these methods without accessing the data or the real data provider (as we are not testing that here).

JustMock has a method that allows you to return any value when calling a method on a mocked object (Returns).  Using this we can mock up our data provider to do something that we want it to do rather than access the database.  This then allows us to return a custom value so the test now becomes:

[TestMethod()]
public void CheckCheckGeneratorReturnsMaximumValue()
{
    // ARRANGE
    // Create a new list of integers to use as the returns value
    var ints = new List { 1, 2, 3, 4, 5 };

    // Create the data provider object and set it up so that GetNumbers method
    // always returns the list we created above.
    var provider = Mock.Create();
    Mock.Arrange(() => provider.GetNumbers(Arg.IsAny<int>())).Returns(ints);

    // ACT
    // Run the NumberChecker method passing in our mocked data provider
    NumberChecker checker = new NumberChecker();
    int actual = checker.Getmax(provider);

    // ASSERT
    int expected = 5;
    Assert.AreEqual(expected, actual);
}

As we can now see passing in the data provider instance from here makes the test much easier to understand.  We have created a new list of integers and are simply returning this from the method on the data provider that we pass in to our NumberChecker.