单元测试样式问题:数据的创建和删除应该采用相同的方法吗?

时间:2009-08-13 08:36:38

标签: unit-testing

我正在为一个在数据库中维护用户的PHP类编写单元测试。我现在想测试创建用户是否有效,以及删除用户是否有效。我看到有多种可能性:

  1. 我只写了一个创建用户的方法,然后删除它
  2. 我写了两种方法。第一个创建用户,保存它的ID。第二个删除具有已保存ID的用户。
  3. 我写了两种方法。第一个只创建一个用户。第二种方法创建一个用户,以便之后可以删除一个用户。
  4. 我已经读过,每个测试方法应该独立于其他测试方法,这意味着第三种可能性是要走的路,但这也意味着每个方法都必须自己设置测试数据(例如,如果你想测试如果可以两次添加用户。)

    你会怎么做?在这种情况下,什么是良好的单元测试风格?

3 个答案:

答案 0 :(得分:7)

两个不同的东西=两个测试。

Test_DeleteUser()也可能位于不同的测试夹具中,因为它具有确保用户已存在的不同Setup()代码。

[SetUp]
public void SetUp() 
{ 
  CreateUser("Me"); 
  Assert.IsTrue( User.Exists("Me"),  "Setup failed!" );
}

[Test]
public void Test_DeleteUser()
{ 
  DeleteUser("Me");
  Assert.IsFalse( User.Exists("Me") );
}

这意味着如果Test_CreateUser()通过且Test_DeleteUser()没有通过 - 您知道代码中负责删除用户的部分中存在错误。

更新:刚刚考虑了Charlie对依赖问题的评论 - 我的意思是如果Creation被破坏,即使删除,两个测试都会失败。我能做的最好的事情是移动一个防护检查,以便安装程序显示在错误和失败选项卡中;区分设置失败(在一般情况下,整个测试夹具显示红色应该很容易发现设置失败。)

答案 1 :(得分:1)

如何依赖于如何利用模拟和存根来实现此目的。我会采用更细粒度的方法进行2次不同的测试。

Test A
  CreateUser("testuser");
  assertTrue(CheckUserInDatabase("testuser"))

Test B
  LoadUserIntoDB("testuser2")
  DeleteUser("testuser2")
  assertFalse(CheckUserInDatabase("testuser2"))

TearDown
  RemoveFromDB("testuser")
  RemoveFromDB("testuser2")

   CheckUserInDatabase(string user)
     ...//Access DAL and check item in DB

如果您使用模拟和存根,则在进行集成测试之前不需要访问DAL,因此在断言和设置数据时不需要做太多工作

答案 2 :(得分:1)

通常,你应该有两种方法,但在下列情况下,现实仍然胜过纸上文字:

您需要大量昂贵的设置代码来创建要测试的对象。这是代码气味,应该修复,但有时,你真的别无选择(想想一些聚合来自几个地方的数据的代码:你真的需要所有这些地方)。在这种情况下,我编写了大型测试(测试用例可以在许多方法上分布数千行代码)。它创建数据库,所有表,用定义的数据填充它们,逐步运行代码,验证每一步。

这应该是一种罕见的情况。如果您需要,您必须主动忽略“测试应该快”的规则。这种情况非常复杂,您希望尽可能多地检查。我有一个案例,我将7个数据库表的内容转储到文件,并比较15个SQL更新中的每一个(这使我在一次测试中比较了105个文件)加上大约一百万个可以运行的断言。

此处的目标是使测试失败,以便您立即注意到问题的根源。这就像将所有约束灌输到代码中并使它们提前失败,以便您知道要检查哪个应用程序代码行。主要缺点是这些测试用例是难以维护的。应用程序代码的每次更改都意味着您必须更新105个“预期数据”文件中的许多文件。