如何为MVC创建具有1对多关联的虚假存储库

Col*_*ICN 6 asp.net-mvc unit-testing repository-pattern

我正在尝试使用具有一对多关系的类对象创建一个虚假的单元测试存储库.我正在使用ASP.NET MVC和Linq to SQL.我的参考文献是Steven Sanderson的"Pro ASP.NET MVC框架"一书.

我用关联创建了实体类:

[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; }
}
Run Code Online (Sandbox Code Playgroud)

以及存储库的抽象接口:

public interface IMusicRepository
{
    IQueryable<Album> Albums { get; }
    IQueryable<Track> Tracks { get; }
}
Run Code Online (Sandbox Code Playgroud)

我能够为专辑创建一个虚假的存储库 - 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(); }
    }

}
Run Code Online (Sandbox Code Playgroud)

只要我不尝试访问任何曲目,哪个工作正常.换句话说,Album.Title可以工作,但访问Album.Tracks.Count()将生成一个空引用异常.我不确定Linq-To-SQL或Linq-To-Objects是否会拾取关联并自动将其与假对象一起使用.

问题在于,无论我尝试什么,似乎都无法将轨道分配给专辑类.我知道EntitySet基于IEnumerable,但是将Enumerable列表强制转换为EntitySet是一个挑战.

相册期待一个EntitySet,但我能够传递的唯一方法是通过自定义帮助:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这跟我一样接近:

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(); }
    }

}
Run Code Online (Sandbox Code Playgroud)

但是,当我尝试访问Album.Tracks时,我得到了

System.NullReferenceException:未将对象引用设置为对象的实例.

这是植入/单元测试:

    [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!");
    }
Run Code Online (Sandbox Code Playgroud)

我花了几天时间寻找这个问题的答案,但没有找到任何例子,所以如果有人能够发布一个有效的答案,我相信其他人也会感兴趣.

我是MVC和单元测试的新手,所以请跟我一起轻松.我手边也有IoC和Moq,但我对这些知之甚少,但我打算同时使用这个项目.如果有更好的方法可以做到这一点,那么我全都耳朵!谢谢.

Dar*_*rov 2

Album门课看起来有点奇怪,尤其是这一部分:

private EntitySet<Track> _tracks;

[Association(Storage = "_tracks", ThisKey = "AlbumID", OtherKey = "AlbumID")]
public EntitySet<Track> Tracks
{
    get { return _tracks; }
    set { _tracks.Assign(value); }
}
Run Code Online (Sandbox Code Playgroud)

私有_tracks成员永远不会被赋值,并且您Assign在抛出异常的 setter 中调用它的方法。顺便说一句,编译器应该警告你。您可以尝试提供默认值或在调用该Assign方法之前检查 setter 中是否为 null:

private EntitySet<Track> _tracks = new EntitySet<Track>();
Run Code Online (Sandbox Code Playgroud)

除此之外,您将 an 转换为 的扩展方法IEnumerable<T>似乎EntitySet<T>不错。

最后,当您分配假专辑时,您需要设置曲目:

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();
Run Code Online (Sandbox Code Playgroud)