我们有返回匹配数据库条目的方法,或者如果不存在匹配则创建条目并返回该条目。使用实体框架。
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 :(得分: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行为来实现的。其他一切都是一块蛋糕。
答案 1 :(得分: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);
}
}
希望它有所帮助!