跳转到主要内容
rli 提交于 17 April 2012

原文链接http://drupal.org/node/1128366

Drupal 7的内核包含了测试的功能。Drupal 的SimpleTest模块是基于SimpleTest PHP Library的。如果您已经很熟系这个框架了,那么您将能更轻松的理解如何把这个框架应用到Drupal中。

Drupal测试相比于单元测试,更偏向于功能测试。功能测试将不会检查个体方法或代码,它偏向于检查测试整体的交互界面功能。这个方法更是用于Drupal。我们既要开始别写一个测试了,我们的测试程序将检查我们模块的四个钩子是不是能够如期的运行。

编写一个好的脆弱是代码的一个挑战是您是否清楚哪些是您需要测试的,您从测试代码中所获得信息并不会告诉您我们的模块到底是如何工作的。这些测试将把您引向一个测试环境其中包含一些假定。想讨论如何设计您的测试程序,请访问SimpleTest教程的Figuring out what we need to test部分。当您熟悉了这个基本部分之后,您将能够分析和理解drupal.org网站的SimpleTest部分其他的教程和示例。

建立测试文件

首先,我们将建立并注册一个测试文档。测试文档的命名必须遵从{modulename}.test的格式。我们在current_posts的目录下建立一个名叫current_posts.test的文件。注意我们的文件只包括php的起始符,并没有结束符。

和数据库API一样,SimpleTest的框架也使用面向对象代码。测试被编写在类里面。就像在Telling Drupal about your module里面讲到的,所有模块必须在.info文件中把自己的类声明。我们把下面的代码添加到current_posts.info文件的结尾:

files[] = current_posts.test

类的继承

接着,我们将添加一个区块,并开始编写测数代码。将下面的代码添加到我们的测试文件中:

<?php /** * @file * Tests for the Current posts module */ class CurrentPostsTestCase extends DrupalWebTestCase{ public static function getInfo(){ return array( 'name' => 'Current posts module functionality', 'description' => 'Tests hooks in the Current posts module', 'group' => 'Current posts', ); } ?>

我们将把我们的测试编写成CurrentPostsTestCase类的方法。注意大写的标准,类名中的每一个单词首字母必须大写。在类的方法名中,第一个单词小写,后面的单词首字母大写。比如getInfo()。下划线和减号是不能用在类名和方法名中的。

基本类DrupalWebTestCase为我们提供了一个测试的基本框架。它建立了一个内部的drupal站点和浏览器之间的联系。请参看API functions来了解它的全部功能。SimpleTest还提供了一个类DrupalUnitTestCase,作为单元测试, 这并不需要drupal bootstrap的支持,换句话说,我们不需要drupal来运行单元测试。

getInfo()方法对drupal的SimpleTest来说是必须的。我们必须有这个方法才能运行。队列里面的元素定义的我们的测试在drupal中的显示。

setUp()方法

下面的方法看上去很简单:

<?php public function setUp(){ parent::setUp('current_posts'); } ?>

这个方法允许我们调用父类中的一个方法来进行必要的设置,我们可以建立一个新用户,给它一些权限,把用户登录。我们在这里要做的就是把我们要安装的模块作为一个参数来传进去。在这里,我们传的是“current_posts“。没有这个方法,我们也可以运行我们的测试,但是一般来讲,我们都需要这个方法来进行一些设置。

第一个测试和module_invoke()

下一步,添加下面的代码:

<?php public function testPermission(){ $data = module_invoke('current_posts', 'permission'); ?>

我们将开始测试我们最简单的一个钩子current_posts_permission()。我们建立一个变量来记录module_invoke()返回的值。这个方法需要两个参数:模块名和钩子名。当测试运行是,它将等同于运行current_posts_permission()。每一个测试都开始与这个方法的调用,

假定

当我们激活了一个钩子,假定assertions将执行实际的测试。一组方法将检查是否符合条件并返回一个布林值。如果是TRUE,测试将通过。反之,测试将失败。它将遵循$this->assertion(condition(s), message)的格式,message是需要被t()方法翻译的,它将在测试运行时显示那一部分被测试了/

把下面的钩子测试方法添加到testPermission()方法:

<?php $this->assertTrue(is_array($data), t('Permission hook returns array.')); $this->assertTrue(array_key_exists('access current_posts content', $data), t('Permission is as expected.')); } ?>

assertTrue 将建查第一个参数是否未TRUE,第二个参数是测试运行结束后将要显示的信息。我们要检查第一个方法是不是返回一个队列,如果我们权限的名字是队列中的key,那么信息将显示我需要的结果。这就是我们测试current_posts_permission()的方法。

测试equality

下面我们来测试menu钩子。添加下面的代码到测试文档:

<?php public function testMenu(){ $items = module_invoke('current_posts', 'menu'); $this->assertEqual(2, count($items), t('Two items in menu.')); $this->assertIdentical($items['admin/config/content/current_posts']['title'], $items['current_posts']['title'], t('Titles are identical.')); } ?>

在激活了模块之后,我们用到另外两个假定。assertEqual()就跟==的作用一样。它假设两个条件是相等的。在这里代表所建立的menu等于2。count($items)会返回menu的个数。

assertIdentical 就像===一样。我们可以用它来检查两个menu的title是否一模一样。

另一个假定

下面是另一个方法的代码:

<?php public function testBlockView(){ $data = module_invoke('current_posts', 'block_view', 'current_posts'); $this->assertEqual(t('Current posts'), $data['subject'], t('Subject is as expected.')); $this->assertNotNull($data['content'], t('Block contains data.')); } ?>

在我们要测试current_posts_block_view()的方法中,我们在module_invoke()中有了第三个参数。这个参数告诉方法在特定时候要调用current_posts方法。我们首先用equality来检查区块的title。我们要小心类似的测试。如果我们改动了区块的title而没有改相应的测试代码,那么这个测试将break。这个方法使用与测试那些不容易改变的变量。

下一步我们用assertNotNull()来检查第一个参数是不是空。我们希望变量中包含信息。

2个负面的假设

添加下面的代码来测试current_posts_block_info():

<?php public function testBlockInfo(){ $info = module_invoke('current_posts', 'block_info'); $this->assertNotEqual('DRUPAL_CACHE_PER_USER', $info['current_posts']['cache'], t('Cache is not set to DRUPAL_CACHE_PER_USER')); $this->assertFalse(count($info) > 1, t('No more than one block defined.')); } ?>

assertNotEqual()相当与假设不等于。将第一个已知的变量和第二个未知的测试变量进行比较。这里我们要检查缓冲是不是没有被设置成DRUPAL_CACHE_PER_USER。注意,在这里我们用了一个附加的队列元素来解析测试变量。

在assertFalse中,我们用一个公式来检查这个方法是不是只定义了一个区块。

激活两个钩子

把下面的代码方法到我们的测试文件中来完成测试的最后一部分:

<?php public function testBlock(){ $info = module_invoke('current_posts', 'block_info'); $data = module_invoke('current_posts', 'block_view', 'current_posts'); $this->assertIdentical($info['current_posts']['info'], $data['subject'], t('Block list name and subject are identical.')); } } ?>

在这里我们激活两个区块方法来比较每个方法中的变量。我们用到了===假定,这是我们测试的最后一部分。

检查

SimpleTest是核心模块的一部分,但是并不在默认安装的模块内。请访问模块页面(http://example.com/admin/modules), 在核心模块的部分查找一个叫做’Testing‘的模块并激活它。在设置页面确保‘Provide verbose information when running tests‘别选择。我们从测试中得到越多的信息越好。

现在激活我们的Current posts模块。如果模块已经激活,那么卸载并重新激活一遍。访问‘Testing’页面(http://example.com/admin/config/development/testing),并在测试列表中查找'Current posts' 。如果您已经运行了测试,您需要在页面的中下方点击'Clean environment'来让我们的测试显示正常。

当一切准备就绪,选择'Current posts'测试的选项并点击'Run tests'按钮。我们将看见一个进度条来显示测试的进度。请耐心,测试模块将用较长时间来建立一个虚拟环境并运行所有的测试。注意,进度条下面的文字将和我们在代码中getInfo()方法的name元素吻合。

当测试结束,我们将看见一个提示信息来告诉我们测试通过,失败,或者有错误抛出。想查看具体信息,请点击测试名下方的链接'Current posts module functionality‘。绿色背景的’pass'表示测试的结果和我们希望的结果相同。红色背景的‘fail'表示结果不想同。黄色背景的’exception'表示测试外有异常抛出,比如PHP警告。如果遇到fail或者exception,请检查您的代码,并重新测试知道通过为止。

查看代码

您可以查看这个测试的代码文件:current_posts.test

参考