2014-09-24 6 views
13

मैं DbSet को link का उपयोग करके एमओक के साथ इकाई ढांचे से नकल करने में सक्षम हूं।Moq इकाई फ्रेमवर्क कैसे करें SqlQuery

हालांकि, अब मैं जानना चाहूंगा कि मैं SqlQuery पर कॉल कैसे कर सकता हूं। यह सुनिश्चित नहीं है कि यह संभव है या कैसे यह मॉक डीबी संदर्भ पर निर्भर करता है कि यह जानने के लिए कि "क्वेरी" क्या कहा जा रहा है।

नीचे मैं जो नकल करने की कोशिश कर रहा हूं।

var myObjects = DbContext.Database 
    .SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", "some_value") 
    .ToList(); 

मैंने वर्तमान में कुछ भी कोशिश नहीं की है क्योंकि यह नहीं पता था कि इस उदाहरण को कैसे शुरू करना है।

DbSet का मजाक और फिर से पुनरावृति करने के लिए, मैं सही ढंग से लौटने MyObject के DbSet नकली कर सकते हैं की लेकिन अब एक SqlQuery कि MyObject की सूची लौटाता है उपहास करने के लिए कोशिश कर रहा हूँ के नीचे है।

var dbContext = new Mock<MyDbContext>(); 
dbContext.Setup(m => m.MyObjects).Returns(mockObjects.Object); 

dbContext.Setup(m => m.Database.SqlQuery... something along these lines 

उत्तर

14

Database.SqlQuery<T> वर्चुअल के रूप में चिह्नित नहीं है, लेकिन Set<T>.SqlQuery वर्चुअल के रूप में चिह्नित किया गया है।

Database.SqlQuery<T> प्रलेखन

इस क्वेरी के परिणामों के संदर्भ भले ही वस्तु के प्रकार लौटे एक इकाई प्रकार है द्वारा कभी नहीं पता लगाया जाता है के आधार पर। संदर्भ द्वारा ट्रैक की गई इकाइयों को वापस करने के लिए 'SqlQuery(String, Object[])' विधि का उपयोग करें।

और Set<T>.SqlQuery प्रलेखन

डिफ़ॉल्ट रूप से, संस्थाओं लौटे संदर्भ द्वारा पता लगाया जाता है; यह को DbRawSqlQuery पर AsNoTracking को कॉल करके बदला जा सकता है।

तो Database.SqlQuery<T>(String, Object[])Set<T>.SqlQuery(String, Object[]).AsNoTracking() (केवल यदि T एफई इकाई है, न कि डीटीओ/वी एम है) के साथ बराबर होना चाहिए।

तो अगर आप में कार्यान्वयन की जगह ले सकता:

var myObjects = DbContext 
    .Set<MyObject>() 
    .SqlQuery("exec [dbo].[my_sproc] {0}", "some_value") 
    .AsNoTracking() 
    .ToList(); 

आप इसे नकली का पालन के रूप में

var list = new[] 
{ 
    new MyObject { Property = "some_value" }, 
    new MyObject { Property = "some_value" }, 
    new MyObject { Property = "another_value" } 
}; 

var setMock = new Mock<DbSet<MyObject>>(); 
setMock.Setup(m => m.SqlQuery(It.IsAny<string>(), It.IsAny<object[]>())) 
    .Returns<string, object[]>((sql, param) => 
    { 
     // Filters by property. 
     var filteredList = param.Length == 1 
      ? list.Where(x => x.Property == param[0] as string) 
      : list; 
     var sqlQueryMock = new Mock<DbSqlQuery<MyObject>>(); 
     sqlQueryMock.Setup(m => m.AsNoTracking()) 
      .Returns(sqlQueryMock.Object); 
     sqlQueryMock.Setup(m => m.GetEnumerator()) 
      .Returns(filteredList.GetEnumerator()); 
     return sqlQueryMock.Object; 
    }); 

var contextMock = new Mock<MyDbContext>(); 
contextMock.Setup(m => m.Set<MyObject>()).Returns(setMock.Object); 
+0

यह मेरे लिए महान काम किया। मेरे लिए, यह एक सहायक में पूछताछ तर्क को दूर करने के लिए बेहतर था, जैसे ऊपर दिए गए स्वीकृत उत्तर में। – JamesWampler

8

Database संपत्ति और SqlQuery विधि virtual के रूप में चिह्नित नहीं कर रहे हैं ताकि वे can't be mocked (Moq का उपयोग कर, आप एक different library कि इस लेकिन यह है कि के लिए खाते में कर सकते हैं अधिक जड़ता आप चाहते हैं उससे अधिक हो सकता है इस्तेमाल कर सकते हैं)।

आप इस तरह के एक सहायक कक्षा में डेटाबेस के पूरे क्वेरी लपेटकर द्वारा के रूप में इस के आसपास पाने के लिए, अमूर्त किसी प्रकार का उपयोग करने की आवश्यकता होगी:

public interface IQueryHelper 
{ 
    IList<MyObject> DoYourQuery(string value); 
} 

public class QueryHelper : IQueryHelper 
{ 
    readonly MyDbContext myDbContext; 

    public QueryHelper(MyDbContext myDbContext) 
    { 
     this.myDbContext = myDbContext; 
    } 

    public IList<MyObject> DoYourQuery(string value) 
    { 
     return myDbContext.Database.SqlQuery<MyObject>("exec [dbo].[my_sproc] {0}", value).ToList(); 
    } 
} 

अब विधि परीक्षण किए जाने हो जाता है:

public void YourMethod() 
{ 
    var myObjects = queryHelper.DoYourQuery("some_value"); 
} 

फिर आप उस कक्षा के निर्माता में IQueryHelper इंजेक्ट करेंगे जिसे आप परीक्षण कर रहे हैं और उस पर नकल करें।

आप DoYourQuery पर परीक्षण कवरेज खोने जा रहे हैं, लेकिन अब क्वेरी so simple there are obviously no deficiencies है।

5

कर सकते हैं आप अपने डेटाबेस संदर्भ के लिए एक आभासी विधि जोड़ सकते हैं कि आप इकाई परीक्षण में ओवरराइड कर सकते हैं :

public partial class MyDatabaseContext : DbContext 
{ 
    /// <summary> 
    /// Allows you to override queries that use the Database property 
    /// </summary> 
    public virtual List<T> SqlQueryVirtual<T>(string query) 
    { 
     return this.Database.SqlQuery<T>(query).ToList(); 
    } 
} 
1

किसी को भी इस पर आना चाहिए। मैंने इसे कुछ दृष्टिकोणों के साथ हल किया। इसे संबोधित करने का एक और तरीका।

  1. मेरा संदर्भ एक इंटरफेस के माध्यम से सारणीबद्ध है। मैं केवल तरीकों में से कुछ की जरूरत है:

    public interface IDatabaseContext 
    { 
        DbSet<T> Set<T>() where T : class; 
        DbEntityEntry<T> Entry<T>(T entity) where T : class; 
        int SaveChanges(); 
        Task<int> SaveChangesAsync(); 
        void AddOrUpdateEntity<TEntity>(params TEntity[] entities) where TEntity : class; 
    

    }

  2. मेरी डेटाबेस का उपयोग के सभी async तरीकों के माध्यम से है। जो इसे नकल करने की कोशिश करते समय समस्याओं का एक नया सेट लाता है। सौभाग्य से - इसका उत्तर दिया गया है here. आपको प्राप्त अपवाद IDbAsyncEnumerable के लिए अनुपलब्ध नकली से संबंधित है। प्रदान किए गए समाधान का उपयोग करके - मैंने इसे थोड़ा और बढ़ा दिया ताकि मेरे पास एक मॉक> ऑब्जेक्ट वापस करने के लिए एक सहायक था जिसने अपेक्षित सभी गुणों का मज़ाक उड़ाया।

    public static Mock<DbSqlQuery<TEntity>> CreateDbSqlQuery<TEntity>(IList<TEntity> data) 
        where TEntity : class, new() 
    { 
        var source = data.AsQueryable(); 
        var mock = new Mock<DbSqlQuery<TEntity>>() {CallBase = true}; 
        mock.As<IQueryable<TEntity>>().Setup(m => m.Expression).Returns(source.Expression); 
        mock.As<IQueryable<TEntity>>().Setup(m => m.ElementType).Returns(source.ElementType); 
        mock.As<IQueryable<TEntity>>().Setup(m => m.GetEnumerator()).Returns(source.GetEnumerator()); 
        mock.As<IQueryable<TEntity>>().Setup(m => m.Provider).Returns(new TestDbAsyncQueryProvider<TEntity>(source.Provider)); 
        mock.As<IDbAsyncEnumerable<TEntity>>().Setup(m => m.GetAsyncEnumerator()).Returns(new TestDbAsyncEnumerator<TEntity>(data.GetEnumerator())); 
        mock.As<IDbSet<TEntity>>().Setup(m => m.Create()).Returns(new TEntity()); 
        mock.As<IDbSet<TEntity>>().Setup(m => m.Add(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Add(i); return i; }); 
        mock.As<IDbSet<TEntity>>().Setup(m => m.Remove(It.IsAny<TEntity>())).Returns<TEntity>(i => { data.Remove(i); return i; }); 
        return mock; 
    } 
    
  3. अंत में - समाधान @Yulium चंद्र द्वारा प्रदान का उपयोग कर - मज़ाक उड़ाया संदर्भ के साथ कच्चे एसक्यूएल के अपने परीक्षण लगता है:

    public Mock<DbSet<TestModel>> MockDbSet { get; } 
    
        .... 
    
        MockDbSet.Setup(x => x.SqlQuery(It.IsAny<string>)) 
          .Returns<string,object[]> 
          ((sql, param) => 
          { 
           var sqlQueryMock = MockHelper.CreateDbSqlQuery(Models); 
    
           sqlQueryMock.Setup(x => x.AsNoTracking()) 
            .Returns(sqlQueryMock.Object); 
    
           return sqlQueryMock.Object; 
          });