2015-11-05 159 views
1

以前有人问过类似的问题,但我不太明白答案。我的具体情况是,我有一个单元测试,它通过REST API端点测试用户注册。然而,用户注册取决于数据库中必须存在的一些记录,否则它将失败。将这些记录插入数据库本身也是一个测试用例。所以我的问题是,为了让记录存在,是否应该按特定顺序执行测试,还是应该在每个依赖于它的测试用例中再次显式插入记录?PHPUnit测试依赖关系

这可能有点不相关,但我使用Laravel 5,因此测试是在PHPUnit中完成的。

回答

1

我要执行我在一个特定的顺序测试,以使 记录存在,或者我应该再次明确在 每一个依赖于它的测试用例插入记录?

我认为这里的正确答案是你不应该这样做(但请继续读下去,虽然不是完美的,但后者可能仍然可以)。

如果你说注册用户本身就是一个测试用例。那么很好,写下那个测试,让我们假设你在接下来的测试中。

创建测试,这样他们才能运行

让我们处理对他们的运行,一旦创建这些行,然后运行多个测试的第一选择。 我认为这是一个非常有缺陷的方法,无论情况如何。突然之间,所有的测试都依赖于另一个测试。假设你在这些行上运行测试A,B和C.也许现在即使是这样,他们中没有一个会改变行。但是,您不可能确定没有任何错误会引入到B中,从而导致数据更改(甚至不是错误,可能只是基础功能发生了更改)。 现在你处于测试C可能通过的情况,但只有在B没有运行之前。这是一个完全不可接受的情况,尤其是在相反的情况下,C只有在B运行时才会通过。 这可能会显示你的应用程序在现实生活中抛出错误的全新安装,而包含一堆数据的开发设置可以工作,所以测试也是如此,因为B在你的数据库中创建了一个特定的状态(也可能随机存在于你的开发数据库)。 然后你给它一些糟糕的客户一下子“选项X”的未设置或初始管理员用户不存在或任何:) =>下策

运行设置为每个测试取决于它

这是一个更好的计划。现在,您至少可以在每次测试中完全控制数据库状态,并且它们都独立运行。 它们的运行顺序不会影响结果 =>

而且,这是为了测试一个子集做一个比较标准的事情。只要继承你的主UnittestCase类,并根据那个东西该功能的子类,像这样所有测试:

abstract class NeedsDbSetupTestCase extends MyAppMainTestCase { 

    function setUp(){ 
    parent::setUp(); 
    $this->setupDb(); 
    } 

    private function setupDb(){ 
    //add your rows and tables and such 
    } 
} 

=>接受的想法

的最佳方法

以上仍有一些缺点。对于一个它是不是一个真正的单元测试了一次,这取决于非常特定的数据库交互,这使得它在准确查明的问题居停少。诚然,虽然这是在许多情况下,更多的是比实际的问题:)

会有什么更大的可能成为一个实际问题,虽然是性能的理论。您正在添加一堆数据库写入,一旦您的测试套装增长,可能需要运行数百次。在项目的开始,这可能意味着,它需要4秒,而不是运行它的2S:P ......一旦项目的发展,你可能会发现自己失去了很多,因为这个时候,虽然。你可能还面临

最后一个问题是,你的测试套件变得依赖它反对在数据库上运行。也许它通过针对MySQL 5.5的运行,并且不符合5.6(学术示例,我猜:P)=>你可能会有各种奇怪的行为,测试通过本地但在CI和失败(有些可能取决于你的设置)。

既然你在这个有趣的更一般的意义,让我勾勒出这个处理一般在这里太:)有道

什么它总是归结为是,像这样的情况会导致你的麻烦:

类用户{ 私人的$ id;公共函数get_data(){ return make_a_sql_call_and_return_row_as_array(“SELECT properta1,propertyb FROM users WHERE id =”。$ this-> id); }

}

现在有些另一种方法是测试实际使用的get_data()的回报,你需要在数据库:)数据...或者你只是嘲笑你的用户对象!

假设您在使用该用户对象的另一个类中有某种方法。 和你的测试看起来有点像这样:

​​

所有你从$用户需要在这里就是返回数组[1,5]。除了插入这个,然后使用用户的一个实例,只需要创建一个模拟:

// this one doesn't do anything yet, returns null on every method. 
$user = $this->getMockBuilder('User')->disableOriginalConstructor()->get_mock(); 
// now just make it return what you want it to return 
$user->method('get_data')->willReturn(array(1,2)); 
// And run your test lightning fast without having ever touched the database but getting the same result :) 
$this->assertTrue(some_method_or_function($user); 

这种方法的另一个隐藏(但有价值的)好处是,设立嘲笑而这样实际上迫使你有关细节深入了解每个班级的行为,最终让您更详细地了解您的应用。 很明显,它的缺点是它(并不总是经常)需要更多的工作来以这种方式编写测试代码,并且这样做的好处可能不值得麻烦。 尤其是在使用其他框架(如WordPress)以及您的代码依赖于其时,实际模拟所有数据库交互可能有点不可行,而现有的库为您的代码提供更慢但更微不足道的数据库测试功能:)

但总的来说选项3是要走的路,选项一是错的,选项二可能是每个人最终在现实生活中所做的:D

+0

非常感谢您的反馈!总是很高兴看到一些真实世界的例子。我想现在我会坚持选择2,因为它直接涉及运行迁移命令(在laravel中易于操作)来填充我的数据库,但我一定会尝试嘲笑,看看这对我有用。 +1 –