如何使用单元测试测试以下方案?

时间:2012-06-06 14:22:49

标签: c# unit-testing

我会对以下单元进行单元测试或进行其他类型的测试:

我想更新数据库中的值,在更新值之后,我想确保使用正确的值更新数据库,但这意味着我必须查询数据库并确定是否存在正确的值和我想在单元测试中,触摸数据库是NO-NO。

我想要对如下方法进行单元测试(db和Update组成):

public void UpdateValue(int value)
{
   db.Update(value);
}

5 个答案:

答案 0 :(得分:3)

您可以简单地测试您的方法是否成功调用了数据库。模拟倾向于关注这一点,即您希望发生的调用实际上

这将涉及用测试版本替换db,以便您对UpdateValue的测试断言它希望使用相同的值调用db.Update(value)

应该测试存储过程或SQL(在一天结束时,它仍然存在与C#代码相同的错误可能性),但可能与您的代码单元测试无关。我们有一个单独的测试项目来测试存储过程逻辑。由于这涉及一个物理数据库,它保持独立和最小 - 但我们仍然认为它是必不可少的。我们已经到了几乎所有数据库脚本都经过测试的阶段,但是首先你可以通过不测试基本的CRUD代码来逃避。任何具有条件语句的SQL都会经过测试。

如果您想进行端到端测试,看看是否可以使用代码保存到数据库并再次获取值,则 不是 < / strong>按定义进行单元测试。作为@lazyberezovsky states,这是集成测试。单元测试旨在消除代码单元周围的所有依赖关系,以便单独测试该单元。

那就是说(在我看来)也必须进行集成测试。我们将它们与用例相对应,以便我们测试用户已签署的用例操作。这样可以一次性破解大量代码,但是具有与共享状态/副作用代码作斗争的明显的缺点。您会发现,通过集成测试,大部分测试都是准备而不是代码的断言。您还发现它们没有识别出失败的特定代码,因此诊断集成测试失败更加困难。

我们通过集成测试取得了中间地带。我们的数据库通过DAL接口提供,我们只是将这个接口(不同于模拟)存根,以便提供内存测试数据而不是物理数据库。这样做的一个缺点是我们错过了对数据库的集成测试。

答案 1 :(得分:1)

您的对象的责任是将值传递给它的依赖项(即数据库)。因此,注入一些抽象,它代表对象的数据库:

public Foo(IDatabase db)
{
   _db = db;
}

并验证此依赖项与您正在测试的对象之间的交互:

Mock<IDatabase> db = new Mock<IDatabase>();
db.Setup(x => x.Update(5));
Foo foo = new Foo(db.Object);
foo.Update(5);
db.VerifyAll();

你是对的,在单元测试中触摸数据库 是NO-NO。单元测试应该只验证一个单元的行为(单独)。如果要检查多个单元一起工作(你的foo和db),那么你需要一个集成测试,它将验证数据库中实际更改的数据。

更新:此示例中使用了Moq框架。

答案 2 :(得分:1)

实际上,根据纯粹主义的定义,触及数据库的测试不是“单元测试”,而是“集成测试”。

典型的答案是用mock替换对db.Update()的调用,但在某些情况下,如果你这样做,可能会觉得你没有测试足够的代码。

根据您使用的数据库,可能还有其他选项。如果使用SQL Server,则可以对本地文件SQL Express db执行单元测试。在每次执行单元测试时,可以使用原始的干净副本覆盖该文件,以便您的测试也变得非常可重复。您需要做的就是使用单元测试代码部署SQL Express数据库,并在单元测试app.config中将连接字符串设置为该本地数据库。

答案 3 :(得分:1)

重要的是要记住,单元测试不是所有自动化软件测试的完美补救措施。

您描述的情况通常以两种方式进行测试:

  • 首先,您将单元测试作为常规开发的一部分,并且此测试将简单地确保使用适当的值调用依赖项(如Adam Houldsworth和lazyberezovsky的答案中所述)。这只是告诉你(和其他开发人员)这段代码就是这样做的,这里是证据
  • 接下来,进行集成测试。此测试需要实际组件和适当的设置(如有效配置,无/最小数量的模拟)。此测试表明您的组件(或整个应用程序)在真实场景下工作和交互。

每种类型的测试都有它的位置,你通常希望同时拥有它们。

答案 4 :(得分:1)

我将使用一个更具体的例子,因为它会更容易讨论。假设UpdateValue实际上是零售系统中的某个地方,用新的美分值更新旧价格。

也许您的班级负责提供当前价格或历史价格 - 让我们称之为PriceProvider。也许它知道如何为收据提供退款价格。也许它知道如何为白色冰箱冰柜提供一系列价格。它还知道如何使用便士提供的新价格更新价格。如果这是它的工作,那么它不应该知道价格的存储位置,因为它已经有一个责任(单一责任原则)。它应该将检索价格的责任委托给其他人,其工作是将业务请求转换为参数化调用,以便知道价格的存储位置。

或者,也许您的班级负责从数据库中检索价格并更新价格 - DatabasePriceRepository。它知道如何查找日期和项目的价格,或者获取日期和类别的价格列表,但它并不关心您为什么要查找它。如果这是你的课程,那么它与数据库强烈耦合 - 这是它的唯一责任 - 所以没有必要嘲笑那个位,或者课程没有价值。相反,您可以通过执行集成测试或编写全栈场景甚至手动测试来测试它。

我曾在我们使用Hibernate的项目中工作,并通过实例化不同的对象来测试配置文件。我们将Hibernate连接到内存数据库来完成它。这非常快,给了我们非常快速的反馈!我还在从UI到数据库的端到端场景的地方工作过,我们通过使用HTTP请求和检查响应来测试某些服务的地方,以及我们手动测试它的其他地方。

无论你是否单元测试这个类,在某些时候你必须在生产中触摸数据库,我相信你会在上线之前测试它。如果您对此类进行单元测试,则需要在其他地方执行集成测试,自动或其他方式。