How to create a fake repository with a 1-to-many association for MVC
asp.net-mvc, repository-pattern, unit-testing
Solution
The `Album` class seems a bit strange and especially this part:
private EntitySet<Track> _tracks;
[Association(Storage = "_tracks", ThisKey = "AlbumID", OtherKey = "AlbumID")]
public EntitySet<Track> Tracks
{
get { return _tracks; }
set { _tracks.Assign(value); }
}
The `_tracks` private member is never assigned a value and you call the `Assign` method on it in the setter which throws the exception. By the way the compiler should have warned you. You could try providing a default value or check for null in the setter before calling the `Assign` method:
private EntitySet<Track> _tracks = new EntitySet<Track>();
Other than that your extension method to convert an `IEnumerable<T>` to `EntitySet<T>` seems nice.
And finally when you assign the fake albums you need to set the tracks:
private static IQueryable<Album> fakeAlbums = new List<Album>
{
new Album
{
AlbumID = 1,
Title = "Jars of Clay",
Tracks = fakeTracks.ToEntitySet()
},
new Album
{
AlbumID = 2,
Title = "Wind in the Wheat",
Tracks = fakeTracks.ToEntitySet()
}
}.AsQueryable();
Problem
I'm trying to create a fake repository for unit testing with a class object that has a one to many relationship. I'm using ASP.NET MVC and Linq to SQL. My reference is Steven Sanderson's "Pro ASP.NET MVC Framework" book. I've created the Entity Classes with the association: ``` [Table(Name = "Albums")] public class Album { [Column(IsDbGenerated = true, IsPrimaryKey = true)] public int AlbumID { get; set; } [Column] public string Title { get; set; } private EntitySet<Track> _tracks; [Association(Storage = "_tracks", ThisKey = "AlbumID", OtherKey = "AlbumID")] public EntitySet<Track> Tracks { get { return _tracks; } set { _tracks.Assign(value); } } } [Table(Name = "Tracks")] public class Track { [Column(IsPrimaryKey = true)] public int TrackID { get; set; } [Column] public string Title { get; set; } [Column] public int AlbumID { get; set; } } ``` And an abstract interface for the repository: ``` public interface IMusicRepository { IQueryable<Album> Albums { get; } IQueryable<Track> Tracks { get; } } ``` I was able to create a fake repository for Albums - FakeMusicRepository: ``` public class FakeMusicRepository : IMusicRepository { private static IEnumerable<Track> fakeTracks = new List<Track> { new Track {AlbumID = 1, TrackID = 1, Title = "Flood"}, new Track {AlbumID = 1, TrackID = 2, Title = "Liquid"}, new Track {AlbumID = 2, TrackID = 1, Title = "Song 1"}, new Track {AlbumID = 2, TrackID = 2, Title = "Song 2"} }.AsEnumerable(); private static IQueryable<Album> fakeAlbums = new List<Album> { new Album {AlbumID = 1, Title = "Jars of Clay"}, new Album {AlbumID = 2, Title = "Wind in the Wheat"} }.AsQueryable(); public IQueryable<Album> Albums { get { return fakeAlbums; } } public IQueryable<Track> Tracks { get { return fakeTracks.AsQueryable(); } } } ``` Which works just fine as long as I don't try to access any tracks. In other words Album.Title works but accessing Album.Tracks.Count() will generate a null Reference exception. I wasn't sure if Linq-To-SQL or Linq-To-Objects would pickup the association and use it automatically with the fake objects or not. The problem is that no matter what I try and can't seem to be able to assign the Tracks to Album class. I know that EntitySet is based on IEnumerable but it's been a challenge to cast the Enumerable list to a EntitySet. Album is expecting an EntitySet but the only way I've been able to pass that in is via a custom helper: ``` public static class EntityCollectionHelper { public static EntitySet<T> ToEntitySet<T>(this IEnumerable<T> source) where T : class { EntitySet<T> set = new EntitySet<T>(); set.AddRange(source); return set; } } ``` This is as close as I've come: ``` public class FakeMusicRepository2 : IMusicRepository { private static IEnumerable<Track> fakeTracks = new List<Track> { new Track {AlbumID = 1, TrackID = 1, Title = "Flood"}, new Track {AlbumID = 1, TrackID = 2, Title = "Liquid"}, new Track {AlbumID = 2, TrackID = 1, Title = "Song 1"}, new Track {AlbumID = 2, TrackID = 2, Title = "Song 2"} }.AsEnumerable(); private static EntitySet<Track> Tracks1 = (from t in fakeTracks where t.AlbumID == 1 select t).ToEntitySet(); private static EntitySet<Track> Tracks2 = (from t in fakeTracks where t.AlbumID == 2 select t).ToEntitySet(); private static IQueryable<Album> fakeAlbums = new List<Album> { new Album {AlbumID = 1, Title = "Jars of Clay", Tracks=Tracks1}, new Album {AlbumID = 2, Title = "Wind in the Wheat", Tracks=Tracks2} }.AsQueryable(); public IQueryable<Album> Albums { get { return fakeAlbums; } } public IQueryable<Track> Tracks { get { return fakeTracks.AsQueryable(); } } } ``` However when I try to access Album.Tracks I get System.NullReferenceException : Object reference not set to an instance of an object. Here is the implamentation / unit test: ``` [Test] public void Test_FakeMusicRepository() { // Arrange: Setup Repository var dc = new FakeMusicRepository2(); // Act: var albums = dc.Albums.AsQueryable(); // Load the first test location Album album = dc.Albums.First(); // Assert: That there are records in the Album Object Assert.Greater(albums.Count(), 0, "No Albums Records Returned!"); //Assert that our first Album is not Null Assert.IsNotNull(album, "returned Album is Null!"); // Assert: That we have the correct Album Assert.AreEqual(album.AlbumID, 1, "Returned Album ID is not correct!"); // Try to Count the related sub table (Tracks) var trackCount = album.Tracks.Count(); // Try to get the first Track var track1 = album.Tracks.FirstOrDefault(); // Assert: The Album has Tracks Assert.Greater(trackCount, 0, "Album has no Tracks"); // Assert: That First track is not Null Assert.IsNotNull(track1, "Track1 object was Null!"); // Assert: That Track1 has data Assert.IsNotNull(track1.TrackID, "Track1's ID was Null!"); } ``` I've spent days searching for an answer to this question and haven't found any examples so if someone would be able to post a working answer example I'm sure others would be interested as well. I am very new to MVC and Unit Testing so please go easy with me. I have IoC and Moq on hand as well but I know even less about these but I'm planning on using both with this project. If there is a better way to do this then I'm all ears! Thanks.