2010-03-01 6 views
6

Estoy tratando de crear un repositorio falso para pruebas unitarias con un objeto de clase que tiene una relación uno a muchos. Estoy usando ASP.NET MVC y Linq a SQL. Mi referencia es el libro "Pro ASP.NET MVC Framework" de Steven Sanderson.Cómo crear un repositorio falso con una asociación 1-to-many para MVC

He creado las clases de entidad con la asociación:

[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; } 
} 

Y una interfaz abstracta para el repositorio:

public interface IMusicRepository 
{ 
    IQueryable<Album> Albums { get; } 
    IQueryable<Track> Tracks { get; } 
} 

yo era capaz de crear un repositorio falso para los álbumes - 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(); } 
    } 

} 

Que funciona bien siempre y cuando no intente acceder a ninguna pista. En otras palabras, Album.Title funciona, pero el acceso a Album.Tracks.Count() generará una excepción de Referencia nula. No estaba seguro de si Linq-To-SQL o Linq-To-Objects recogían la asociación y la usaban automáticamente con los objetos falsos o no.

El problema es que no importa lo que intento y parece que no puedo asignar la clase de Pistas a Álbum. Sé que EntitySet está basado en IEnumerable, pero ha sido un desafío lanzar la lista de Enumerable a un EntitySet.

álbum está a la espera de una EntitySet pero la única manera que he sido capaz de pasar de que en es a través de un ayudante de encargo:

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

Esto es lo más cerca que he llegado:

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

} 

sin embargo cuando intento de acceso Album.Tracks me sale

System.NullReferenceException: referencia a objeto no establecida como una instancia de una junta bject.

Aquí está la prueba implamentation/unidad:

[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!"); 
    } 

He pasado días en busca de una respuesta a esta pregunta y no he encontrado ningún ejemplo de modo que si alguien sería capaz de publicar un trabajo ejemplo de respuesta Estoy seguro de que otros también estarían interesados.

Soy muy nuevo en MVC y pruebas unitarias, así que por favor vaya fácil conmigo. También tengo IoC y Moq a mano, pero sé mucho menos sobre esto, pero estoy planeando usar ambos con este proyecto. Si hay una mejor manera de hacerlo, ¡soy todo oídos! Gracias.

+0

¿Tiene el código que está utilizando para acceder a las pistas? ES DECIR. su código de implementación? Eso podría ayudar a la depuración. Si está haciendo Album.Tracks.Count(), entonces la razón podría ser que no está asignando las pistas correctamente, sino que depende del código de implementación. – Odd

+0

Buena idea: he publicado mi código de prueba/implementación de la unidad. – ColinICN

Respuesta

2

La clase Album parece un poco extraño y sobre todo esta parte:

private EntitySet<Track> _tracks; 

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

El miembro privado _tracks es Nunca asignado un valor y se llama al método Assign en él en la incubadora, que produce la excepción. Por cierto, el compilador debería haberte advertido.Usted podría tratar de proporcionar un valor por defecto o detectar nula en la incubadora antes de llamar al método Assign:

private EntitySet<Track> _tracks = new EntitySet<Track>(); 

Aparte de que su método de extensión para convertir un IEnumerable<T>-EntitySet<T> parece agradable.

Y finalmente cuando asigne los discos falsos es necesario configurar las pistas:

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(); 
+0

Gracias Darivn: agregar _tracks = new EntitySet (); resuelto. Como dijiste, no estaba asignando un valor a _tracks. Los ejemplos de MSDN y los libros que tengo muestran el código de esta manera sin la asignación, supongo que Linq To Sql se ocupa de eso por usted. Sí, el compier estaba lanzando advertencias, pero debido a que el Linq a SQL estaba funcionando, pensé que todo estaba bien. Tiene razón al olvidar asignar Pistas en el objeto Álbum. Me perdí pegar en un bloque de código que muestra eso. Agregué el bloque de código faltante y también arrojaré mi código de implementación de prueba unitaria. ¡Gracias! – ColinICN

1

Aquí es la corregida (de trabajo) Clase de álbum:

[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 = new EntitySet<Track>(); 

    [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; } 
} 

El problme era que yo no estaba' t creando una instancia del objeto Album como señaló Darin. La prueba unitaria ahora funciona correctamente. ¡He probado tanto con el repositorio falso como con los datos en vivo y todo funciona! Es extraño que los ejemplos de MSDN (http://msdn.microsoft.com/en-us/library/bb425822.aspx) tampoco mostraran la asignación.

Cuestiones relacionadas