如何测试从数据库返回数据的函数

时间:2010-11-15 21:56:03

标签: asp.net unit-testing linq-to-sql

说我有一个功能:

public List<Car> GetFourWheelDrives()
{
   return dataContext.Cars.Where(c => c.IsFourWheelDrive == true).ToList();
}

首先,我在这里尝试做的很明显。关于我如何做这个的任何建议?有什么改进吗?

所以现在我想对此进行单元测试。那它是一个单元测试吗?单位测试不是要想触摸数据库吗?那么这是一个功能测试吗?

所以说我写了一个测试

[Test]
public void GetFourWheelDrives_NoParameters_ReturnAListOfOnlyCarsThatAreFourWheelDrives
{
   Assert.Greater(dataContext.Cars.Where(c => c.IsFourWheelDrive == false).ToList().Count, 0);

   var cars = GetFourWheelDrives();

   Assert.Greater(cars.Count, 0);

   Assert.AreEqual(cars.Count, cars.Where(c => c.IsFourWheelDrive == true).ToList().Count);
}

首先断言我确保数据库中存在非4WD的汽车,否则它将不是有效的测试。

第二断言我确定至少有一个被退回。

第三断言我确保返回的所有车辆实际上都是4WD。

我知道这不是一个好的测试,因为我在测试中使用与我正在测试的函数相同的逻辑。然而,这是我目前可以想到的唯一方法。

有人能告诉我如何正确测试吗?

谢谢!

2 个答案:

答案 0 :(得分:1)

嗯,据TDD的一所学校说,这正是你应该做的;编写包含您正在开发的逻辑的测试,然后将逻辑从测试中重构为生产类。

但是,我不会将您正在进行的单元测试调用,因为按定义进行的单元测试不应该触及外部资源,如数据库,文件系统和其他“集成点”。这些都是“集成测试”,尽管它们同样有用,但您应该总是尝试编写最无副作用的测试,以证明代码按设计工作。

看看你正在测试什么。基本上,您希望确保该函数查询您的Linq数据源以查找四轮驱动的汽车,而不是任何其他类型的汽车。假设只有一分钟,Linq2SQL完全清楚如何将这个Linq语句转换为针对您的数据库的SQL查询。你不需要测试;您需要测试的是,根据数据源的代码进行正确的查询。

为此,您应该实现模拟。模拟是一个代表外部资源的测试对象,可以告诉它期望对其方法进行某些调用并以某种方式运行。在这里,您应该模拟数据源对象。为了能够这样做,包含GetFourWheelDrives()的类必须从其作用域之外给出该对象;如果它是new()s up,你与Linq2SQL提供程序“紧密耦合”并且无法编写真正的单元测试(如果可以将Linq2SQL指向测试数据库,你仍然可以将其编写为集成测试)。鉴于您可以传入所使用的数据源,您应该为其提供一个模拟,希望访问其Cars属性,并返回一个Cars对象数组作为IQueryable。您可以定义此数组,这意味着您可以控制执行查询的数据,因此您可以测试查询是否返回了应该返回的对象。

使用Rhino.Mocks,您的代码可能如下所示:

[Test] 
public void GetFourWheelDrives_NoParameters_ReturnAListOfOnlyCarsThatAreFourWheelDrives 
{ 
   var dataSource = MockRepository.GenerateMock<IDataContext>();
   var carsArray = new []{new Car{IsFourWheelDrive = false}, new Car{IsFourWheelDrive = true}};

   dataSource.Expect(ds=>ds.Cars).Return(carsArray.AsQueryable()).Repeat.AtLeastOnce();

   var objectToTest = new TestObject(dataSource); 
   var cars = objectToTest.GetFourWheelDrives(); 

   Assert.AreEqual(carArray.Count(c=>c.IsFourWheelDrive), cars.Count);

   foreach(var car in cars)
        Assert.IsTrue(car.IsFourWheelDrive);
} 

这个测试有效地证明了给定一组任意大小的汽车,你的功能将只返回那些4WD的汽车,而不需要真正的DB这样做。在您测试的类依赖于外部代码的情况下,这就是您的单元测试应该是什么样的。

答案 1 :(得分:0)

数据库单元测试不是纯粹的单元测试,但绝对值得做。

常用方法是使用空数据库 - 所以不要对数据库做任何假设 - 并且测试的部分设置是

  • (清除数据库)
  • 输入4WD记录
  • 拨打电话

通常你必须用INSERT检查SELECT。