2014-11-20 28 views
14

Come si simula il metodo AsNoTracking?
Nell'esempio seguente, DbContext si è iniettato nella classe di servizio. Funziona correttamente se rimuovo il metodo di estensione AsNoTracking dal metodo GetOrderedProducts, ma con il test AsNoTracking non riesce perché restituisce null. Ho anche provato a prendere in giro AsNoTracking per restituire il valore corretto ma non ha funzionato.Mock AsNoTracking Entity Framework

rendimenti
public interface IUnitOfWork 
{ 
    IDbSet<TEntity> Set<TEntity>() where TEntity : class; 
    int SaveAllChanges(); 
} 

public class Entites : DbContext, IUnitOfWork 
{ 
    public virtual DbSet<Product> Products { get; set; } // This is virtual because Moq needs to override the behaviour 

    public new virtual IDbSet<TEntity> Set<TEntity>() where TEntity : class // This is virtual because Moq needs to override the behaviour 
    { 
     return base.Set<TEntity>(); 
    } 

    public int SaveAllChanges() 
    { 
     return base.SaveChanges(); 
    } 
} 

    public class ProductService 
{ 
    private readonly IDbSet<Product> _products; 
    private readonly IUnitOfWork _uow; 

    public ProductService(IUnitOfWork uow) 
    { 
     _uow = uow; 
     _products = _uow.Set<Product>(); 
    } 
    public IEnumerable<Product> GetOrderedProducts() 
    { 
     return _products.AsNoTracking().OrderBy(x => x.Name).ToList(); 
    } 
} 

    [TestFixture] 
public class ProductServiceTest 
{ 
    private readonly ProductService _productService; 

    public ProductServiceTest() 
    { 
     IQueryable<Product> data = GetRoadNetworks().AsQueryable(); 
     var mockSet = new Mock<DbSet<Product>>(); 
     mockSet.As<IQueryable<Product>>().Setup(m => m.Provider).Returns(data.Provider); 
     mockSet.As<IQueryable<Product>>().Setup(m => m.Expression).Returns(data.Expression); 
     mockSet.As<IQueryable<Product>>().Setup(m => m.ElementType).Returns(data.ElementType); 
     mockSet.As<IQueryable<Product>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator()); 
     var context = new Mock<Entites>(); 
     context.Setup(c => c.Products).Returns(mockSet.Object); 
     context.Setup(m => m.Set<Product>()).Returns(mockSet.Object); 
     context.Setup(c => c.Products.AsNoTracking()).Returns(mockSet.Object); 
     _productService = new ProductService(context.Object); 
    } 

    private IEnumerable<Product> GetRoadNetworks() 
    { 
     return new List<Product> 
     { 
      new Product 
      { 
       Id = 1, 
       Name = "A" 
      }, 
      new Product 
      { 
       Id = 2, 
       Name = "B" 
      }, 
      new Product 
      { 
       Id = 1, 
       Name = "C" 
      } 
     }; 
    } 

    [Test] 
    public void GetOrderedProductTest() 
    { 
     IEnumerable<Product> products = _productService.GetOrderedProducts(); 
     List<string> names = products.Select(x => x.Name).ToList(); 
     var expected = new List<string> {"A", "B", "C"}; 
     CollectionAssert.AreEqual(names, expected); 
    } 
} 

Il problema è AsNoTracking NULL nella prova di unità enter image description here

+0

Le osservazioni suggeriscono qualcosa di diverso da nullo dovrebbe essere restituito: http://msdn.microsoft.com/en-us/library/gg679352%28v= vs.103% 29.aspx –

+0

@ ta.speot.is Esatto, ma restituisce null. – Shahin

risposta

36

Guardando il source code del metodo AsNoTracking() estensione:

public static IQueryable AsNoTracking(this IQueryable source) 
{ 
    var asDbQuery = source as DbQuery; 
    return asDbQuery != null ? asDbQuery.AsNoTracking() : CommonAsNoTracking(source); 
} 

Dal source (il vostro DbSet<Product> si sta cercando di prendere in giro) è davvero un DbQuery (perché DbSet è derivante dalla DbQuery), si tenta di richiamare il metodo "reale" (non deriso) AsNoTracking() che giustamente restituisce null.

Provare a deridere il metodo AsNoTracking() così:

mockSet.Setup(x => x.AsNoTracking()).Returns(mockSet.Object); 
+0

Questo non funziona su dotnet/EF core: l'espressione fa riferimento a un metodo che non appartiene all'oggetto mocked: x => x.AsNoTracking (). Immagino perché AsNoTracking() non sia più su IQueryable ma invece su "Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions"? Qualche idea su una soluzione? – Bassebus

+0

@Bassebus, non ho familiarità con EF core per rispondere alla domanda, ma potrebbe meritare una nuova domanda – haim770

+0

ok, grazie. Ho fatto un po 'più googling e penso che questo potrebbe essere il problema/soluzione https://github.com/aspnet/EntityFramework/issues/7937 – Bassebus

3

Hai:

context.Setup(c => c.Products).Returns(mockSet.Object); 
context.Setup(m => m.Set<Product>()).Returns(mockSet.Object); 
context.Setup(c => c.Products.AsNoTracking()).Returns(mockSet.Object); 

ma ricordate che i metodi di estensione sono solo zucchero sintattico. Quindi:

c.Products.AsNoTracking() 

è in realtà solo:

System.Data.Entity.DbExtensions.AsNoTracking(c.Products) 

quindi la configurazione finto sopra è privo di significato.

La domanda è ciò che lo statico DbExtensions.AsNoTracking(source) method effettivamente fa al suo argomento. Vedi anche the thread What difference does .AsNoTracking() make?

Cosa succede se si rimuove il Setup che interessa AsNoTracking dalla classe di test?

Potrebbe essere utile dare a tutti i tuoi scherzi MockBehavior.Strict. In tal caso scoprirai se i membri che il metodo statico invoca su di essi, sono raggirabili da Moq (cioè metodi/proprietà virtuali in senso generale). Forse si può prendere in giro il non-statico method DbQuery.AsNoTracking se necessario.

+0

Grazie, ho bisogno di utilizzare AsNoTracking per migliorare le prestazioni della query. Sto cercando un modo per risolvere questo problema di eccezioni null quando utilizzo AsNoTracking nelle classi di servizio. – Shahin

+0

Nel mio EF (versione 6.0) non è sempre un metodo di estensione ma un metodo virtuale definito in 'DbQuery <>'. Solo quando fai riferimento ai tuoi dati come "IQueryable <>", dovrai utilizzare il metodo di estensione. – haim770

+1

@ haim770 Hai assolutamente ragione. La tua risposta è molto più precisa della mia, anche se ho accennato al metodo non statico su 'DbQuery <>' nell'ultima frase. Ripeterò che, se avesse usato le canzonette di 'Strict', avrebbe avuto un'eccezione che gli diceva quale metodo aveva bisogno di deridere, invece di aspettare solo che fosse sorto il' NullReferenceException'. Le persone usano troppo spesso le sciocchezze. –