2016-03-12 52 views
0

我們有方法返回一個匹配的數據庫條目,或者如果不存在匹配,則創建條目並返回該條目。使用實體框架。如何測試依賴於數據庫內容的方法?

public Transaction FindOrCreateTransactionByID(string id, DBContext db) 
{ 
    Transaction t = db.Transactions.SingleOrDefault(f => f.TransactionID == id); 
    if(t == null) 
    { 
     t = new Transaction { TransactionID = id }; 
     db.Transactions.Add(t); 
     db.SaveChanges(); 
    } 
    return t; 
} 

還有更多的方法比上面,但它應該說明情況。

我們應該試着嘲笑DBContext嗎?通過DbSet[Transactions]和模擬,而不是?將方法分解爲Find()Create()而不是?

+0

您是否閱讀過[this](https://msdn.microsoft.com/en-us/library/dn314429.aspx?f=255&MSPPError=-2147217396)文章?你使用哪個版本的EF? –

回答

0

我試圖複製你的代碼在本地編寫簡單的測試你在問題中提到的方法。下面是我的了:

public class Transaction 
{ 
    public string TransactionID { get; set; } 
} 

public class DBContext : DbContext 
{ 
    public virtual DbSet<Transaction> Transactions { get; set; } 
} 

public class TransactionService 
{ 
    public Transaction FindOrCreateTransactionByID(string id, DBContext db) 
    { 
     Transaction t = db.Transactions.SingleOrDefault(f => f.TransactionID == id); 
     if (t == null) 
     { 
      t = new Transaction { TransactionID = id }; 
      db.Transactions.Add(t); 
      db.SaveChanges(); 
     } 
     return t; 
    } 
} 

[TestFixture] 
public class TransactionServiceTests 
{ 
    [Test] 
    public void When_transaction_not_found_new_transaction_created() 
    { 
     const string id = "_id_"; 

     var mockSet = new Mock<DbSet<Transaction>>(); 

     // setup data 
     IQueryable<Transaction> data = new List<Transaction>().AsQueryable(); 
     mockSet.As<IQueryable<Transaction>>().Setup(m => m.Provider).Returns(data.Provider); 
     mockSet.As<IQueryable<Transaction>>().Setup(m => m.Expression).Returns(data.Expression); 
     mockSet.As<IQueryable<Transaction>>().Setup(m => m.ElementType).Returns(data.ElementType); 
     mockSet.As<IQueryable<Transaction>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator); 

     var mockContext = new Mock<DBContext>(); 
     mockContext.SetupGet(x => x.Transactions).Returns(mockSet.Object); 

     var service = new TransactionService(); 
     service.FindOrCreateTransactionByID(id, mockContext.Object); 

     mockSet.Verify(
      set => set.Add(It.Is<Transaction>(t => t.TransactionID == id)), 
      Times.Once); 
     mockContext.Verify(context => context.SaveChanges(), Times.Once); 
    } 
} 

測試已經使用NUnit編寫的,但你可以與你心愛的單元測試框架來代替它沒有任何麻煩。 最困難的部分是弄清楚如何模擬事務DbSet,因此SingleOrDefault調用實際上不會拋出ArgumentNullException。正如你所看到的,這是通過模擬事務的IQueryable行爲來完成的。其他的一切都是一塊蛋糕。

0

我已經通過Typemock Isolator爲你做一些測試實例,這使得它更容易嘲笑的DbContext:

public class Transaction 
{ 
    public string TransactionID { get; set; } 
    public string TransactionName { get; set; } 
} 

public class DBContext : DbContext 
{ 
    public DbSet<Transaction> Transactions { get; set; } 
} 

public class TransactionService 
{ 
    public Transaction FindOrCreateTransactionByID(string id, DBContext db) 
    { 
     Transaction t = db.Transactions.SingleOrDefault(f => f.TransactionID == id); 
     if (t == null) 
     { 
      t = new Transaction { TransactionID = id }; 
      db.Transactions.Add(t); 
      db.SaveChanges(); 
     } 
     return t; 
    } 
} 

[TestClass] 
public class UnitTest 
{ 
    [TestMethod, Isolated] 
    public void TestTransactionExist() 
    { 
     var service = new TransactionService();   
     string id = "id"; 
     string name = "name"; 

     var fakeDb = new DBContext(); 
     var fakeDbSet = Isolate.Fake.Instance<DbSet<Transaction>>(); 

     List<Transaction> data = new List<Transaction>() 
     { 
      new Transaction { TransactionID = id, TransactionName = name } 
     }; 

     Isolate.WhenCalled(() => fakeDb.Transactions).WillReturnCollectionValuesOf(data.AsQueryable()); 

     Transaction res = service.FindOrCreateTransactionByID(id, fakeDb); 

     Assert.AreEqual(name, res.TransactionName); 
    } 

    [TestMethod, Isolated] 
    public void TestNewTransaction() 
    { 
     var service = new TransactionService(); 
     string id = "id"; 

     var fakeDb = new DBContext(); 
     var fakeDbSet = Isolate.Fake.Instance<DbSet<Transaction>>(); 

     List<Transaction> data = new List<Transaction>(); 

     Isolate.WhenCalled(() => fakeDb.Transactions).WillReturnCollectionValuesOf(data.AsQueryable()); 
     Isolate.WhenCalled(() => fakeDb.Transactions.Add(null)).DoInstead(
     context => 
     { 
      data.Add(context.Parameters[0] as Transaction); 
      return context.Parameters[0] as Transaction; 
     }); 

     Transaction res = service.FindOrCreateTransactionByID(id, fakeDb); 

     Assert.AreEqual(id, res.TransactionID); 
    } 
} 

希望它能幫助!

相關問題