2015-11-26 7 views
7

फेंकता है मैं एक परियोजना के लिए स्वचालित परीक्षण लिख रहा हूं जिसे मैं एमवीसी, एंटिटीफ्रेमवर्क (कोड पहले), यूनिट परीक्षण और मोक से अधिक परिचित होने के लिए काम कर रहा हूं।स्वचालित परीक्षणों में DbContext.ChangeTracker SQLException

public void RepoSaveChanges() 
{ 
    foreach(var entry in _dbContext.ChangeTracker.Entities().Where(e => e.State == EntityState.Modified)) 
    { 
     MyModelBase model = entry.Entity as MyModelBase; 
     if (model != null) 
     { 
      model.LastModified = DateTime.Now; 
     } 
    } 
    _dbContext.SaveChanges(); 
} 

इस दौरान ठीक काम करता है:

मैं अपने Repository कक्षा में एक खंड जो मेरे मॉडल का एक LastModified फ़ील्ड सेट करता है जब भी Repository.SaveChanges() नियंत्रक है कि इस तरह काम करता है (MyModelBase एक आधार वर्ग है) द्वारा कहा जाता है सामान्य ऑनलाइन वातावरण में एप्लिकेशन रन टाइम, लेकिन परीक्षण विधियों में चलते समय यह टूट जाता है। मैं डीबीसीएन्टेक्स्ट में डीबीसेट्स को नकल करने के लिए मोक का उपयोग करता हूं और अपना टेस्ट डेटा सेट करता हूं।

यहाँ मेरे लिए अजीब हिस्सा है: मेरे इकाई परीक्षण ठीक (पारित) चलाने पर वे कभी वास्तव में foreach पाश में प्रवेश नहीं करते - यह लटका हुआ है जब ChangeTracker.Entities() एक्सेस किया जाता है और पाश इस्तीफा, _dbContext.SaveChanges() करने के लिए नीचे कूद। कोई त्रुटि नहीं है

हालांकि, एक दोस्त की मशीन पर जो मेरे साथ प्रोजेक्ट साझा करता है, उसे 0Qएक्सेस होने पर एक एसक्यूलेक्सप्शन मिलता है। मेरे पास VS2015 में फेंकने वाले एसक्यूएलएक्सप्सेप्शन हैं और मेरे पक्ष में अपवाद का कोई आउटपुट या अन्य संकेत नहीं है।

Result StackTrace:
at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternal& connection) ....

Result Message:
Test method MyProject.Tests.TestClasses.MyControllerTests.TestCreate threw exception: System.Data.SqlClient.SqlException: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)

अंत में, मेरे सवाल है: वहाँ Moq ChangeTracker (मैं पहले जांच से नहीं संदेह है) उपहास करने के लिए उपयोग करने के लिए एक रास्ता है, या वहाँ एक और दृष्टिकोण मैं अपने RepoSaveChanges() कि स्वचालित रूप से एक संपत्ति सेट करने के लिए ले सकता है? ChangeTracker.Entities() तक पहुँचने के बिना मैं हर मॉडल प्रकार यह है कि के लिए LastModified क्षेत्र स्थापित करने के लिए अद्यतन तर्क है की आवश्यकता होगी। इसी तरह, मैं क्योंकि परीक्षण जिद्दी किया जा रहा है आदर्श नहीं है कि एपीआई/रूपरेखा का हिस्सा का उपयोग न की तरह लग रहा है।

किसी को क्यों SQLException फेंक नहीं है/मेरी मशीन पर पकड़ा नहीं किया जा सकता के रूप में किसी भी जानकारी है? या इकाई परीक्षणों में ChangeTracker.Entities() का उपयोग करने के तरीके पर कोई सुझाव? मैं केवल एक अंतिम उपाय के रूप में मेरे सभी मॉडल और नियंत्रकों भर में अलग-अलग LastModified गुण सेट होगा।

अद्यतन: अधिक नमूना कोड इतना अनुरोध किया गया है मुझे अधिक विस्तार में जाने दें। मैं moq का उपयोग कर एक DbContext नकली, तो DbSet वस्तुओं DbContext में निहित नकली:

var mockContext = new Mock<MyApplicationDbContext>(); //MyApplicationDbContext extends DbContext 

Person p = new Person(); 
p.Name = "Bob"; 
p.Employer = "Superstore"; 

List<Person> list = new List<Person>(); 
list.Add(p); 

var queryable = list.AsQueryable(); 

Mock<DbSet<Person>> mockPersonSet = new Mock<DbSet<Person>>(); 
mockPersonSet.As<IQueryable<Person>>().Setup(set => set.Provider).Returns(queryable.Provider); 
mockPersonSet.As<IQueryable<Person>>().Setup(set => set.Expression).Returns(queryable.Expression); 
mockPersonSet.As<IQueryable<Person>>().Setup(set => set.ElementType).Returns(queryable.ElementType); 
mockPersonSet.As<IQueryable<Person>>().Setup(set => set.GetEnumerator()).Returns(() => queryable.GetEnumerator()); 

DbSet<Person> dbSet = mockPersonSet.Object as DbSet<Person>; 
mockPersonSet.Setup(set => set.Local).Returns(dbSet.Local); 

mockContext.Setup(context => context.Set<Person>()).Returns(mockPersonSet.Object); 
mockContext.Setup(context => context.Persons).Returns(mockPersonSet.Object)); 

//Create the repo using the mock data context I created 
PersonRepository repo = new PersonRepository(mockContext.Object); 

//Then finally create the controller and perform the test 
PersonController controller = new PersonController(repo); 
var result = controller.Create(someEmployerID); //Sometimes throws an SQLException when DbContext.SaveChanges() is called 
+0

ईएफ का कौन सा संस्करण यह है? –

+0

@ ई-बैट ईएफ संस्करण 6.1.3 – Softerware

उत्तर

2

मैं अपने लिए आदर्श समाधान से कम पर पहुंच गया लेकिन मुझे आगे बढ़ने में सक्षम बनाने के लिए काफी अच्छा था।

public class MyApplicationDbContext : IdentityDbContext<MyApplicationUser> 
{ 
    //DbSets etc 

    public virtual IEnumerable<MyModelBase> AddedEntries 
    { 
     get 
     {    
      foreach (var entry in ChangeTracker.Entries().Where(entry => entry.State == EntityState.Added)) 
      { 
       MyModelBase model = entry.Entity as MyModelBase; 
       if (model != null) 
       { 
        yield return model; 
       } 
      } 
     } 
    } 
} 

इस तरह फोन करके समस्या बयान में वर्णित के रूप में मैं अभी भी प्रविष्टियां() व्यापार तर्क के लिए पुनरावृति कर सकते हैं: मैं बस मेरी कक्षा कि DbContextMyApplicationDbContext बुलाया फैली करने के लिए अमूर्त के एक स्तर जोड़कर DbContext.ChangeTracker.Entries() एपीआई उन्हें धोखा दिया MyApplicationDbContext.ChangeTracker.Entries() के बजाय।

List<SomeModel> addedEntries = new List<SomeModel>(); 
addedEntries.add(someModelWhichWillBeAddedByTheController); 
mockContext.Setup(context => context.AddedEntries).Returns(addedEntries); 

इस तरह नियंत्रक someModelWhichWillBeAddedByTheController का पालन करेंगे जब AddedEntries संपत्ति प्रयोग किया जाता है: हालांकि, बाद से मैं संपत्ति virtual बनाया है, मैं एक वापसी Moq का उपयोग कर सेट कर सकते हैं। नीचे की ओर यह है कि मैं वास्तविक व्यापार तर्क में उपयोग किए गए DbContext.ChangeTracker.Entries() कोड का परीक्षण नहीं कर सकता, लेकिन मैं बाद में परीक्षण डेटाबेस के साथ एकीकरण परीक्षण को लागू करके इसे प्राप्त करने में सक्षम हूं।

मैं कभी भी SQLException को एक मशीन पर फेंकने का कारण नहीं ढूंढ पाया था, न कि दूसरे।

0

क्या अपने त्रुटि कह रहा है कि यह आपके डेटाबेस से संबंध नहीं बना सकते है। यह आपके DbContext के निर्माण पर होता है जहां एफई की जांच की तरह काम करने के लिए अपने डेटाबेस माइग्रेट करने के लिए की जरूरत है शुरू कर देंगे।

ऐसा लगता है कि आपको अपने dbcontext के निर्माता को भी नकल करने की आवश्यकता है। मैं moq के साथ खुद से परिचित नहीं हूं, लेकिन अगर मैं सही ढंग से अपना कोड पढ़ता हूं तो मैं आपको कन्स्ट्रक्टर का मज़ाक उड़ाता नहीं देखता हूं।

+0

मुझे कन्स्ट्रक्टर का मज़ाक करने में कोई मूल्य नहीं दिखता है क्योंकि मेरे परीक्षण किसी भी डेटाबेस से कनेक्ट नहीं होंगे, मुझे DbContext.ChangeTracker.Entries() API ऑफ़लाइन का उपयोग करने में सक्षम होना चाहिए । सबसे अच्छा मैं यह करने के लिए एक वैकल्पिक कनेक्शन स्ट्रिंग प्रदान करने के लिए कनेक्ट करने में असफल रहा है। – Softerware

+0

मेरा मुद्दा यह है कि एक DbContext डेटाबेस कनेक्शन की अपेक्षा करता है। यदि आप एक मॉक किए गए डीबीकॉन्टेक्स्ट चाहते हैं तो आपको कन्स्ट्रक्टर का नकल करना चाहिए ताकि जब आपका डीबीकॉन्टेक्स्ट बनाया गया हो तो आपको नकली संस्करण मिल जाएगा - डेटाबेस कनेक्शन के बिना – Batavia

0

अपवाद कहता है कि यह डीबी से कनेक्शन नहीं बना सकता है, मुझे संदेह है कि आप और आपके मित्र के पास app.config फ़ाइलों में अलग-अलग कनेक्शन स्ट्रिंग हैं, या आपके मित्र को डीबी तक पहुंच नहीं है।

वास्तव में आप एक एकीकरण परीक्षण, नहीं इकाई परीक्षण लिख रहे हैं। यूनिट परीक्षण विशेष रूप से परीक्षण के तहत किसी ऑब्जेक्ट के लिए लिखे जाते हैं।अपने मामले कोड में:

PersonController controller = new PersonController(); 
var result = controller.Create(someEmployerID); 

Repository के वास्तविक कार्यान्वयन का उपयोग नहीं करना चाहिए। आपको PersonController में भंडार के मॉक किए गए इंस्टेंस को इंजेक्ट करने की आवश्यकता है। मुझे लगता है कि आप किसी भी IoC containtainers उपयोग नहीं कर रहे तो अपने नियंत्रक को इसकी सुई देने यदि आप किसी अन्य निर्माता जोड़ सकते हैं सक्षम होने के लिए मान लेते हैं:,

private IRepository _repository;  
public PersonController() : this(new Repository()) //real implementation 
{} 

public PersonController(IRepository repository) 
{ 
    _repository = repository; 
} 

// your test 
var reporitory = new Mock<IRepository>(); 
var controller = new PersonController(repository.Object); 
controller.CreateEmployee(someId); 
// assert that your repository was called 
repository.Verify(...); 

इस तकनीक Poor Man's injection कहा जाता है यह इंजेक्शन की सिफारिश की तरीका नहीं है, लेकिन यह है केवल ठोस उदाहरण होने से बेहतर है।

इसके बाद आप तो आप जो आपके DbContext के चारों ओर एक आवरण हो जाएगा IDbContext की तरह एक अंतरफलक की आवश्यकता है अपने Repository के लिए इकाई परीक्षण (नहीं एकीकरण) लिखने के लिए चाहते हैं। और अपने Repository के लिए PersonController - पैरामीटर-कम और IDbContext के साथ 2 रचनाकार बनाएं।

अद्यतन: उपेक्षा मेरा आखिरी Repository और DbContext के बारे में बयान। मैंने प्रलेखन की जांच की है DbChangeTracker.Entries() विधि वर्चुअल नहीं है, जिसका अर्थ है कि आप इसे Moq लाइब्रेरी का उपयोग करके नकल करने में सक्षम नहीं होंगे। आपको another mocking framework का उपयोग करने की आवश्यकता है या इंटरगेशन परीक्षणों (मॉक किए गए उदाहरणों के बिना) का उपयोग करके इसका परीक्षण करना होगा।

+0

हाय, आपके इनपुट के लिए धन्यवाद। असल में मैंने उस हिस्से को नमूना कोड से त्रुटि में छोड़ दिया। मैं एक भंडार का उपयोग करता हूं जो मुझे अपने मॉक किए गए डीबीकॉन्टेक्स्ट को पास करने की अनुमति देता है। यह आपके जैसा उल्लेख किया गया है "वास्तविक कार्यान्वयन" नहीं है। नियंत्रक पर मेरे सभी HTTP GET विधियों को रिपोजिटरी ठीक से काम करते हैं, समस्या तब आती है जब रेपो में कुछ तर्क डीबीकॉन्टेक्स्ट का उपयोग करने का प्रयास करते हैं।ChangeTracker.Entries() एपीआई। – Softerware

संबंधित मुद्दे