C# 使用Linq到SQL的单元测试实践
我正试图对单元测试进行思考,我遇到了一个我不确定的行为:C# 使用Linq到SQL的单元测试实践,c#,.net,unit-testing,linq-to-sql,tdd,C#,.net,Unit Testing,Linq To Sql,Tdd,我正试图对单元测试进行思考,我遇到了一个我不确定的行为: ID = i.ID, ItemName = i.ItemName, /* etc, etc, etc */ HistoryDate = historyDate “可以备份库存” ID = i.ID, ItemName = i.ItemName, /* etc, etc, etc */ HistoryDate = historyDate 基本上,“Inventory”表被复制到“InventoryHistory”表中,并给出备份发生的时
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
“可以备份库存”
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
基本上,“Inventory”表被复制到“InventoryHistory”表中,并给出备份发生的时间戳(“HistoryDate”)
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
以下是备份库存的代码:
DateTime historyDate = DateTime.Now;
MyDataContext db = new MyDataContext();
db.GetTable<InventoryHistory>().InsertAllOnSubmit(
db.GetTable<Inventory>()
.Select(i => new InventoryHistory
{
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
})
);
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
DateTime historyDate=DateTime.Now;
MyDataContext db=新的MyDataContext();
db.GetTable().InsertAllOnSubmit(
db.GetTable()
.选择(i=>new Inventory History
{
ID=i.ID,
ItemName=i.ItemName,
/*等等等等等等*/
HistoryDate=历史日期
})
);
我的问题是:
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
我想问的问题是,这真的是一个单元测试吗?单元测试会考虑嘲讽<代码>表>代码>实例,因为我们不关心实际数据,而是创建项目的机制是正确的。
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
在上面的代码片段中,您似乎是在对Linq方法本身进行单元测试,而不是自己编写的任何特定代码
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
至于你的最后一个问题,模仿的一个基本错误是假设模仿时要测试什么。通常情况下,您将模拟要测试的类型所使用的某个对象。例如:
public ICalculatorService
{
int Add(int a, int b);
}
[Test]
public void CannAdd()
{
var mock = Mock<ICalculatorService();
mock.Setup(m => m.Add(It.IsAny<int>(), It.IsAny<int>()))
.Returns(100);
var service = mock.Object;
Assert(service.Add(1, 2) == 100); // Incorrect
}
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
公共iCalculator服务
{
整数相加(整数a、整数b);
}
[测试]
公共添加()
{
var mock=mock m.Add(It.IsAny(),It.IsAny())
.申报表(100份);
var service=mock.Object;
断言(service.Add(1,2)==100);//不正确
}
上面的测试毫无意义,因为我正在测试它是否返回了我告诉它的内容。我不是在测试Moq框架,我需要测试我的代码,所以我需要测试消费者:
public class Calculator
{
private readonly ICalculatorService _service;
public Calculator(ICalculatorService service)
{
_service = service;
}
public int Add(int a, int b)
{
return _service.Add(a, b);
}
}
[Test]
public void CannAdd()
{
var mock = Mock<ICalculatorService();
mock.Setup(m => m.Add(It.IsAny<int>(), It.IsAny<int>()))
.Returns(100);
var calculator = new Calculator(mock.Object);
Assert(calculator.Add(1, 2) == 100); // Correct
}
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
公共类计算器
{
专用只读ICalculatorService_服务;
公共计算器(iCalculator服务)
{
_服务=服务;
}
公共整数相加(整数a、整数b)
{
返回服务。添加(a,b);
}
}
[测试]
公共添加()
{
var mock=mock m.Add(It.IsAny(),It.IsAny())
.申报表(100份);
var计算器=新计算器(mock.Object);
Assert(calculator.Add(1,2)==100);//正确
}
这更像是一个简单的例子。我现在正在测试计算器
消费者本身,而不是消费品。在您的示例中,即使您模拟DataContext以返回表的虚拟实例
,您能得到什么真正的好处
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
实际上,您可能会创建一个存储库,例如
IInventoryRepository
,并创建该存储库的使用者(可以是域模型、控制器等)。然后通过测试,您可以模拟该存储库,并测试您的消费者。对我来说,这看起来是一个相当原子化的操作,没有多少机会将其分解
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
单元测试不会影响数据库——这是集成测试。如果您想对此进行一个良好的单元测试,您应该测试行为——在本例中,历史记录在应该备份的时候进行备份 通过全面披露,我才刚刚开始学习EF和LINQ以及测试它们的最佳方法,所以你可能会得到一些关于它们的更有用的信息,所以这些只是一般的测试答案
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
一,。
我看不出有什么方法可以进一步细分,以隔离单元测试,除了:
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
被重构成一个单独的方法进行单元测试,因为唯一的其他代码是LINQ调用,由MS负责测试
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
二,。
我认为您无法引入seam来将其与抽象存储库工厂模式隔离,因为您正在调用datacontext
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
我不确定你是否应该伪造这个(因为你要测试它将是一个模拟的正确的-一个你测试的伪造,一个你不测试的伪造是一个存根),但是因为它正在调用一个测试服务器,您可以将其设置为自动化集成测试,因为该功能涉及到与数据存储的交互。首先,您描述的方法看起来很简单,我不确定它是否需要任何单元测试。
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
但是,如果你想分解它,你可以这样做:
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
IQueryable GetInventoryForBackup(此DataContext上下文)
{
返回context.GetTable();
}ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
IEnumerable到InventoryHistory(此IEnumerable数据,日期时间历史日期)
{
返回数据。选择(i=>newinventoryHistory{ID=i.ID…)。。。。
}ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
void SaveHistory(IEnumerable数据)
{
dataContext.InsertAllOnSubmit(数据);
dataContext.SubmitChanges();
}ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate
现在,您已经有了一些合适的方法,您可以轻松地编写单元测试。谢谢,这是非常有用的,尽管我对此还不太清楚。根据您的回答,我的总结是“can备份库存”的单元可测试性行为将取决于消费的环境?这只是有点令人沮丧,因为我看到了“可以备份库存”作为一种非常简单的行为,尽管就单元测试而言,它更加模糊。@Eric-如果您需要模拟数据上下文,那么您的总结是正确的。问题是您的特定代码段实际上是非常原子化的,为了从单元测试中获得任何价值,您需要模拟数据c
ID = i.ID,
ItemName = i.ItemName,
/* etc, etc, etc */
HistoryDate = historyDate