Me gustaría dar a los antecedentes de esta cuestión. Omitir si te gusta. Durante bastante tiempo he prestado mucha atención al debate en stackoverflow y en otra parte con respecto a las pruebas de código, como se relaciona con EF. Uno de los campamentos dice, prueba directamente en una base de datos debido a las diferencias entre el Linq to Objects & Sql y las implementaciones. Otra dice que la prueba por la que se burlaba.

Otra división en la opinión, es la cuestión de la utilización de repositorios, o aceptar que DbContext y DbSet ya proporcionar una unidad de trabajo y modelo de repositorio. En el tiempo que he estado usando EF, he probado con todas las combinaciones de las opiniones ofrecidas por estos campos. Independientemente de lo que yo he hecho, EF resulta difícil prueba.

Yo estaba emocionada de encontrar el equipo EF hizo DbSet más mockable en EF 6. También se proporcionaron documentación sobre cómo burlarse de DbSet, incluyendo los métodos de async el uso de Moq. En el trabajo de mi último proyecto con la participación de la Api de Web me di cuenta de que si podía burlarse de EF, yo podría saltar a escribir repositorios, como la razón normal de la escritura de ellos es hacer las cosas comprobables. La inspiración vino después de leer un par de posts en el blog como este…

–End de fondo —

El problema real es que, siguiendo el ejemplo de código dado por el equipo EF sobre cómo Moq DbSet, si .Include() se utiliza en cualquier código, un ArgumentNullException es lanzado.

Otros post relacionado en por LO

Aquí está mi interfaz de DbContext:

public interface ITubingForcesDbContext
{
    DbSet<WellEntity> Wells { get; set; }

    int SaveChanges();

    Task<int> SaveChangesAsync();

    Task<int> SaveChangesAsync(CancellationToken cancellationToken);
}

Esta es la principal entidad que mi controlador de ofertas con

Aquí es el controlador que utiliza directamente un EF DbContext

Por último aquí está la prueba fallida…

De La Configuración De Prueba—

   [ClassInitialize]
public static void ClassInitialize(TestContext testContest)
{
var well1 = new WellEntity { Name = "Well 1" };
var well2 = new WellEntity { Name = "Well 2" };
var well3 = new WellEntity { Name = "Well 3" };
var well4 = new WellEntity { Name = "Well 4" };
well1.GeometryItems.Add(new GeometryItem());
well1.TemperaturePoints.Add(new TemperaturePoint());
well1.SurveyPoints.Add(new SurveyPoint());
well2.GeometryItems.Add(new GeometryItem());
well2.TemperaturePoints.Add(new TemperaturePoint());
well2.SurveyPoints.Add(new SurveyPoint());
well3.GeometryItems.Add(new GeometryItem());
well3.TemperaturePoints.Add(new TemperaturePoint());
well3.SurveyPoints.Add(new SurveyPoint());
well4.GeometryItems.Add(new GeometryItem());
well4.TemperaturePoints.Add(new TemperaturePoint());
well4.SurveyPoints.Add(new SurveyPoint());
var wells = new List<WellEntity> { well1, well2, well3, well4 }.AsQueryable();
var mockWells = CreateMockSet(wells);
_mockContext = new Mock<ITubingForcesDbContext>();
_mockContext.Setup(c => c.Wells).Returns(mockWells.Object);
}
private static Mock<DbSet<T>> CreateMockSet<T>(IQueryable<T> data) where T : class
{
var mockSet = new Mock<DbSet<T>>();
mockSet.As<IDbAsyncEnumerable<T>>()
.Setup(m => m.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<T>(data.GetEnumerator()));
mockSet.As<IQueryable<T>>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider<T>(data.Provider));
mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.Expression);
mockSet.As<IQueryable<T>>().Setup(m =>m.ElementType).Returns(data.ElementType);
mockSet.As<IQueryable<T>>().Setup(m=>m.GetEnumerator()).
Returns(data.GetEnumerator());
return mockSet;
}
[TestMethod]  
public async Task Get_ById_ReturnsWellWithAllChildData()
{
//Arrange
var controller = new WellsController(_mockContext.Object);
//Act
var actionResult = await controller.Get(1);
//Assert
var response = actionResult as OkNegotiatedContentResult<WellModel>;
Assert.IsNotNull(response);
Assert.IsNotNull(response.Content.GeometryItems);
Assert.IsNotNull(response.Content.SurveyPoints);
Assert.IsNotNull(response.Content.TemperaturePoints);
}

TestDbAsyncQueryProvider & TestDbAsyncEnumerator vienen directamente de los que se hace referencia EF equipo de documentación. He probado con diferentes variaciones para ¿cómo puedo crear los datos para el simulacro, no hemos tenido ninguna suerte con ella.

  • He intentado configurar async prueba (Añadido a Github del proyecto : github.com/pauldambra/includeTests) y funcionan bien con la misma burla como usted han usado. Eres capaz de empujar un ejemplo de fallo a Github o agregar más detalles en donde la prueba está fallando. Esto se siente como en su instalación, en realidad tiene un valor nulo en las que no debe ser…
  • Hola Pablo, gracias por tu ayuda con esto… Para hacer su ejemplo, representan mi código, tienes que cambiar IDbSet a DbSet, y que le obligará a cambiar su CreateMockSet método para que coincida con lo que he publicado anteriormente. A continuación podrá ver el mismo comportamiento que estoy viendo, que es el Sistema.ArgumentNullException en var queryTask = esperan mockContext.Objeto.Los padres de familia.Incluyen(p => p.Los niños).FirstAsync();
  • Los métodos en DbSet no son virtuales, lo que significa que cuando Moq burla de la clase no puede reemplazar los métodos. IDbSet existe (al menos parcialmente) para permitir que usted para burlarse más fácilmente. ¿Por qué quieres usar DbSet y no IDbSet?
  • En el fondo, hay un enlace «DbSet más mockable’, en que el artículo se describe dice.. «Incluso si usted quiere crear su propio falsificaciones (o de la prueba de dobles) en EF6, usted puede hacer eso con DbSet ahora, no IDbSet. IDbSet todavía está allí para compatibilidad hacia atrás.» Con EF 6 ellos quieren que usted burlándose de DbSet, no IDbSet, y cuando miré todos los DbSet miembros están marcados como virtual.
  • Aquí hay alguien con el mismo problema. social.msdn.microsoft.com/Forums/en-US/…
  • Ha, por lo que son! Ellos no aparecen como virtual en la página principal para DbSet y yo no profundizar en los métodos de sí mismo… mi mal
  • Sí, Incluyen no Virtual…

InformationsquelleAutor GetFuzzy | 2013-12-11

5 Comentarios

  1. 38

    Para cualquier persona que se topa con este tema con interés en cómo resolver el .Include("Foo") problema con NSubstitute y Entity Framework 6+, yo era capaz de pasar por mi Include llamadas de la siguiente manera:

    var data = new List<Foo>()
    {
    /* Stub data */
    }.AsQueryable();
    var mockSet = Substitute.For<DbSet<Foo>, IQueryable<Foo>>();
    ((IQueryable<Post>)mockSet).Provider.Returns(data.Provider);
    ((IQueryable<Post>)mockSet).Expression.Returns(data.Expression);
    ((IQueryable<Post>)mockSet).ElementType.Returns(data.ElementType);
    ((IQueryable<Post>)mockSet).GetEnumerator().Returns(data.GetEnumerator());
    //The following line bypasses the Include call.
    mockSet.Include(Arg.Any<string>()).Returns(mockSet);
    • Usted puede utilizar reemplazar «Bar» con Ella.IsAny<Cadena>() para que sea más general. A continuación, puede llamar .Incluyen(x=>x.Bar) porque las llamadas .Include(«Bar») internamente
    • En realidad, esto no funciona para mí, pero tal vez no estoy siguiendo exactamente. ¿Te refieres a usar .Include(Args.Any<string>()) en sustituto de .Include("Bar"), porque este no me funciona 🙁
    • Lo siento, estoy usando el Moq. Pero sí de entender correctamente. La línea que yo uso y que me funciona es: dbSet.Setup(m => m.Include(It.IsAny<String>())).Returns(dbSet.Object); donde dbSet = new Mock<DbSet<T>>(); tal vez la razón por la que no me echa para IQueryable < t > es relevante.
    • Nope, esto no funciona. Mientras que la creación, lanza un Sistema.NotSupportedException diciendo que la Expresión hace referencia a un método que no pertenecen a un objeto de burla objeto. Cualquier idea sobre cómo solucionar este problema y conseguir que funcione ?
    • Bueno tengo alrededor de esa excepción. La clave para evitar esto es no desechar la dbset como un iQueryable < t > como los guías oficiales del estado.
    • Agregado @SalmanHasratKhan ‘s la solución a la respuesta.
    • lo que realmente funciona, la llamada «include» se omite
    • Discúlpenme chicos/chicas, ¿qué es el ‘Post’ en este contexto? Estoy en lo cierto al suponer que debería ser ‘Foo’?
    • La prueba falla como se tira un error: configuración no válida en un método de extensión: x => x.Incluir<Velocidad>(Es.IsAny<Cadena>())

  2. 31

    Aquí hay un ejemplo completo del uso de Moq. Puede pegar el ejemplo completo dentro de su unidad de clase de prueba. Gracias a los comentarios de @jbaum012 y @Skuli. También recomiendo el excelente tutorial de Microsoft.

    //An Address entity
    public class Address
    {
    public int Id { get; set; }
    public string Line1 { get; set; }
    }
    //A Person referencing Address
    public class Person
    {
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual Address Address { get; set; }
    }
    //A DbContext with persons and devices
    //Note use of virtual (see the tutorial reference)
    public class PersonContext : DbContext
    {
    public virtual DbSet<Person> Persons { get; set; }
    public virtual DbSet<Address> Addresses { get; set; }
    }
    //A simple class to test
    //The dbcontext is injected into the controller
    public class PersonsController
    {
    private readonly PersonContext _personContext;
    public PersonsController(PersonContext personContext)
    {
    _personContext = personContext;
    }
    public IEnumerable<Person> GetPersons()
    {
    return _personContext.Persons.Include("Address").ToList();
    }
    }
    //Test the controller above
    [TestMethod]
    public void GetPersonsTest()
    {
    var address = new Address { Id = 1, Line1 = "123 Main St." };
    var expectedPersons = new List<Person>
    {
    new Person { Id = 1, Address = address, Name = "John" },
    new Person { Id = 2, Address = address, Name = "John Jr." },
    };
    var mockPersonSet = GetMockDbSet(expectedPersons.AsQueryable());
    mockPersonSet.Setup(m => m.Include("Address")).Returns(mockPersonSet.Object);
    var mockPersonContext = new Mock<PersonContext>();
    mockPersonContext.Setup(o => o.Persons).Returns(mockPersonSet.Object);
    //test the controller GetPersons() method, which leverages Include()
    var controller = new PersonsController(mockPersonContext.Object);
    var actualPersons = controller.GetPersons();
    CollectionAssert.AreEqual(expectedPersons, actualPersons.ToList());
    }
    //a helper to make dbset queryable
    private Mock<DbSet<T>> GetMockDbSet<T>(IQueryable<T> entities) where T : class
    {
    var mockSet = new Mock<DbSet<T>>();
    mockSet.As<IQueryable<T>>().Setup(m => m.Provider).Returns(entities.Provider);
    mockSet.As<IQueryable<T>>().Setup(m => m.Expression).Returns(entities.Expression);
    mockSet.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(entities.ElementType);
    mockSet.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(entities.GetEnumerator());
    return mockSet;
    }
    • La línea específica que hizo el truco para mí fue mockPersonSet.El programa de instalación(m => m.Include(«Dirección»)).Devuelve(mockPersonSet.Objeto);
  3. 8

    Con este juego y hacer referencia a las respuestas aquí El programa de instalación resultado de llamar al método de extensión parece Moq no puede burlarse de la estática de los métodos de extensión

    He intentado añadir:

    mockSet.Setup(t => t.FirstAsync()).Returns(Task.FromResult(data.First()));
    mockSet.Setup(t => t.FirstAsync(It.IsAny<Expression<Func<T, bool>>>())).Returns(Task.FromResult(data.First()));

    Y Moq queja de que:

    Sistema.NotSupportedException : Expresión hace referencia a un método que
    no pertenece a un objeto de burla objeto: t => t.FirstAsync()

    Así que parece que hay tres opciones:

    1. refactorizar el código para aislar aún más dbcontext, así que usted no tiene que
      prueba de este comportamiento
    2. interruptor de DbSet a IDbSet en lugar de burlarse de
      DbContext
    3. permitir que las pruebas para crear un SQL de base de datos compacto y
      rellenar con los datos con el fin de ejecutar las pruebas
    • Pablo, gracias por tu ayuda, yo había llegado a esa conclusión de la pasada noche, y me fui hacia atrás y miró en lo que estoy haciendo para ver si es realmente necesario utilizar .Incluye (a) a todos. He optado por la carga diferida, como estoy haciendo esto para un web API controlador, y cuando puedo generar mis modelos a enviar sobre el alambre, las propiedades de navegación que se accede. Es decir, las propiedades de navegación desencadenar la segunda consulta a la base de datos, antes de que el objeto se dirige a través del cable. Hasta ahora esto se ve aceptable de rendimiento sabio y me permite continuar con este camino fácil, a prueba de… Deseo de EF fue más fácil para probar…
    • Hola Pablo, una nota más, alguien del equipo EF volvieron a mí, no es obvio, pero al parecer estas cosas puede ser burlado… echa un vistazo a «Moq_DbSet_can_be_used_for_query_with_include_extension_method_that_does_something» en MockableDbSetTests en la EF FunctionalTests proyecto. Gracias, Arthur
    • Para cualquier persona que llegue tarde a esta discusión. La solución que se ofrece aquí: entityframework.codeplex.com/SourceControl/latest#test/… utiliza el Include sobrecarga que acepta un string no la lambda de sobrecarga.
    • Estrictamente hablando, esta respuesta es errónea ahora la pregunta acerca de Enity Marco 6, y baum012 la respuesta de obras para que (+ ver Skuli comentario para Moq versión)
    • Así, es de dos años de edad. 🙂
    • Estoy tratando de poner a prueba un método que llama: .Include(n => n.Something.Select( q => q.SomethingElse)); Alguna idea?

  4. 0

    El ejemplo DbSet proporcionada por las EF el equipo, es sólo eso: un ejemplo.

    Si desea burlarse de Include (o FindAsync), tendrás que hacerlo tú mismo.

    • Puede haber perdido algo (consulte github.com/pauldambra/includeTests), pero usted no tiene que burlarse de Incluir – hay que admitir que en realidad no hace nada con una colección en la memoria…
  5. 0

    Me las arreglé para burlarse de Incluir en Moq con un enfoque genérico. Aunque esto no cubre todos los usos de Include(), sólo con cadena y de Expresión, pero que se ajustaba a mis necesidades:

    public Mock<DbSet<T>> SetupMockSetFor<T>(Expression<Func<DbContext, DbSet<T>>> selector) where T : class
    {
    var mock = new Mock<DbSet<T>>();
    mock.ResetCalls();
    this.EntitiesMock.Setup(m => m.Set<T>()).Returns(mock.Object);
    this.EntitiesMock.Setup(selector).Returns(mock.Object);
    mock.Setup(x => x.Include(It.IsAny<string>())).Returns(mock.Object);
    try
    {
    mock.Setup(x => x.Include(It.IsAny<Expression<Func<T, object>>>()))
    .Returns(mock.Object);
    }
    catch
    {
    //Include only applies to some objects, ignore where it doesn't work
    }
    return mock;
    }

    prueba de uso:

            var mockCourseSet = SetupMockSetFor(entities => entities.Courses);

    En el método de servicio:

    var foundCourses = dbContext.Courses.Include(c => c.CourseParticipants).Where(c => c.Id = courseId)

Dejar respuesta

Please enter your comment!
Please enter your name here