原文链接http://drupal.org/simpletest-tutorial。
注:本指南中引用代码来自Examples for Developers module,你可以:
- 从该模块中复制一份代码,编辑、修改然后进行测试.
- 报告所发现问题,进行修复,欢迎进行补丁修正和提出改进意见。
通过本指南,可以让你掌握基本的测试方法。学习本节后你可以建立第一个测试!在本例中,我们将创建一个叫“simplest example”的dummy 模块,它提供一个叫”simplest_example” 的内容类型。 这个内容类型和任何基本drupal节点类型(如:’page’)一样。本指南将阐述如何对这个simplest_example内容类型进行测试,从而保证其功能的有效实现。
准备工作
首先,我们要确保simpletest模块已安装。在Drupal6中,simpletest属于contributed模块。你需要阅读模块目录下的INSTALL.txt文件,按说明操作,其中包括打补丁。
如果你还没有开始准备,请确保Simpletest模块已经启用。
学习本指南需要打开Simpletest Verbose开关。它能给出每一个测试阶段的Drupal页面截图。该选项在管理/配置/开发/设置(admin/config/development/testing/settings)下可以看到。
本指南所采用的例子取自http://drupal.org/project/examples
Drupal Simpletest如何工作
大多数Drupal功能是面向web的,因此演习这些功能是及其重要的。Simpletest生成一份完整的安装和一个虚拟的web浏览器,通过这个虚拟浏览器来进行一系列测试,就如你手工一步步进行一样。记住每次测试都是在运行一个完整的新的durpal实例是非常重要的。换而言之,每一个测试实例是空的,没有进行过任何配置,建立过任何用户,除了默认的内核模块,其他模块都未开启。如果你的测试需要一个有特权的用户,你就需要创建一个(这如同你手工创建一个测试环境一样)。如果有需要的模块,你就需要开启他们。如果需要配置,就要用simpletest来进行配置,因为在我们要测试的druapl实例中,该测试站点还未进行过任何配置,文件目录下也没有任何文件,也未安装过可选模块,也没有创建过任何用户。在simpletest中,我们有神奇的命令去执行它们,稍后我们会做进一步讲解。
关于Simpletest Example模块
Simpletest Example模块给出了一个可配置的节点类型(如’page’或者’story’).该类型有一个标题和一个正文组成。借此它给我们演示了测试内容创建的机会。为了实现这个节点,我们需要用hook_node_info()钩子函数来创建节点类型,用hook_form()函数来创建表单。我们需要为模块设置权限(为此你需要” create simpletest_example content”权限来创建,或者由”edit own simpletest_example content”取得编剧权限)。当然,我们需要准备一个simpletest_example.info文件。
注意,我们的模块中有一个bug:它的权限处理是有问题的。因此即使给予了’edit own simpletest_example’ 的字符串,simpletest_example_access()函数也不能正确处理,所以当我们赋予用户编辑节点权限室,实际上是不能实现的。当然,手工测试时可能一直用用户1来测试,不会看到测试失败的情况,我们稍后会提到。
本代码在Examples for Developers模块中维护,它有助于在正式发布前,用dummy代码来进行测试、编辑和实践,.建议开发者取些模块,启用并练习。
思考何时需要测试
如果已经安装了simpletest_example模块,你可以按如下步骤一步步来进行,并思考何时需要测试。
访问创建内容->Simpletest Example (Create Content > Simpletest Example),你会看到如下页面。
查看界面并选定要测试的内容。
仔细浏览确保你已经熟知接口间是如何工作的,在一个基本实例,而不是Simpletest操作中,功能可以实现。如果你还没有理解原理,是不能写好测试代码的。在本例中,我们来填上标题和正文内容,然后按保存按钮。你将看到如下页面。
对simpletest_example构建测试
好,我们可以来测试了,我们会在simpletest_example.test文件中操作。(该文件可从下载的模块中获得)
如果对一个模块添加了一个新的.test文件,你可能需要重刷Drupal缓存,让Drupal知道你有新文件了。要刷新缓存,可以去admin/settings/performance下点击“clear all chaches”按钮。
进行一个测试需要如下四个步骤:
- 创建结构(即产生一个从DrupalWebTestCase继承的类)
- 对任何用户创建或需要的配置的测试实例初始化
- 用测试实例产生真实测试
- 当然,要特别想清楚为什么我们的测试没有按预想地进行并调试测试(也许还有模块).
在开始前,我们需要模块扩展DrupalWebTestCase.
为了使Simpletest接口调用测试,我们来用getInfo()函数实现。它提供了用户接口信息,并在刷新缓存后,显示在simpletest页面上。
下面就是非常重要的setup()函数了。这里我们就要处理必须处理的,让该Drupal实例按我们的想法工作。我们必须考虑:“我们如何操作,才能从初始化安装到可以在哪里运行测试?在我们的实例中,我们知道必须:
- 启用Simletest Exmaple模块
- 创建一个有权限的用户,来产生一个simpletest_example 节点
- 用该用户登陆
以上方法就是有setup()方法来实现的.
<?php
public function setUp() {
// Enable any modules required for the test.
parent::setUp('simpletest_example');
// Create and log in our privileged user.
$this->privileged_user = $this->drupalCreateUser(array('create simpletest_example', 'edit own simpletest_example'));
$this->drupalLogin($this->privileged_user);
}
?>
注:我们必须启用所有关联模块。本例中没有,但是你的模块肯能有这种情况。这可以通过给parent::setup()传递多个参数的方法来实现。在有些情况下,必须按顺序来启用(比如:date_api需要在data 前启用)。举例如下: <?php
public function setUp() {
// Must include every single module on which Meetings module relies.
parent::setUp(
'content',
'ctools',
'date_api',
'date',
'date_timezone',
'date_popup',
'features',
'filefield',
'token',
'messaging',
'notifications',
'notifications_content',
'notifications_team',
'strongarm',
'text',
'views',
'votingapi',
'meetings'
);
}
?>
测试建立:创建节点
现在让我们来创建测试实例练习模块。刚才我们已经为我们的测试类建立了成员函数,每一个成员我们会来练习一个测试。
我们的第一个测试将通过node/add/simpletest-example中的form来创建一个新的simpletest_example节点.
<?php
public function testSimpleTestExampleCreate() {
// Create node to edit.
$edit = array();
$edit['title'] = $this->randomName(8);
$edit['body'] = $this->randomName(16);
$this->drupalPost('node/add/simpletest-example', $edit, t('Save'));
$this->assertText(t('Simpletest Example Node Type @title has been created.', array('@title' => $edit['title'])));
}
?>
drupalPost, drupalGet和判断语句
以上代码在node/add/simpletest-example页面完成了一个简单的递交表单的功能。它将字段定义到了一个数列中($edit数组产生随机值赋给title和body),然后递交表单,并声明在页面上找到了合适的文本.
大多数的测试将按如下形式来进行:
- 调用drupalGet()函数来跳转到一个页面或者用drupalPost()来递交表单
- 用一次或多次语句来检查内容是所见即所得的.
$this->drupalGet($path)可以非常容易的跳到指定页面。
$this->drupalPost($path, $edit_fields, $submit_button_name)稍复杂些。
还有几十种这样的语句。最容易的一个是$this->assertText($text_to_find_on_page).当超越本指南知识后,你会想参阅更多的语句。
通过Web界面运行Simpletest
下面我们来运行测试。这里我们将用web界面来运行。
访问管理->站点构建->测试(Admin->Site Building->Testing)。选择你要创建的测试-它会在”Examples”组下,点击并运行”Runtests”(有时可能需要清除Drupal缓存才能在列表中见到)
测试运行后你就能见到是否通过(pass)的结果。
你也可以通过命令行运行。更多的内容请参阅通过命令行来运行测试。
一个失败测试的演示
测试成功的例子没有必要多讲了,现在我们来举一个失败的例子。
还是用相同的类,这次来测试编辑节点的情况。我们的模块有一个缺陷-它不能很好的处理”edit own simpletest_exple”权限,当用户仅有此权限时缺不能编辑它。在本实例中我们将创建一个节点,然后来进行编辑。
运行测试并查看结果。
<?php
public function testSimpleTestExampleEdit() {
$settings = array(
'type' => 'simpletest_example',
'title' => $this->randomName(32),
'body' => $this->randomName(64),
);
$node = $this->drupalCreateNode($settings);
// For debugging, we might output the node structure with $this->verbose().
// It would only be output if the testing settings had 'verbose' set.
$this->verbose('Node created: ' . var_export($node, TRUE));
if (!$this->runningOnTestbot()) {
// Make sure we don't get a 401 unauthorized response when editing.
$this->drupalGet("node/{$node->nid}/edit");
$this->assertResponse(200, t('User is allowed to edit the content.'));
// Looking for title text in the page to determine whether we were
// successful opening edit form. Note that the output message
// "Found title in edit form" is not translated with t().
$this->assertText(t("@title", array('@title' => $settings['title'])), "Found title in edit form");
}
}
?>
单元测试
Simpletest也提供了DrupalUnitTestCase,来作为DrupalWebTestCase的可选类.
单元测试不会产生数据库表格和文件目录. 所以单元测试要比功能测试初始化快,但这也意味着它不能访问数据库和文件目录。调用任何一个需要数据库看函数会产生异常。这些函数包括routine 函数,比如像watchdog(), drupal_function_exists(), module_implements(), module_invoke_all() 等。
更多的编写单元测试的资料请参阅用Simpletest来进行单元测试。
何时在Simpletest中用t()函数
和Drupal中的大多数字符串不同,simpletest的字符串不经常用到t()函数。这里有两个原因:
- 许多simpletest的字符串仅仅是测试目的,不会用到网站上,因此不需要解释它们。
- 当不同组件尽可能分开时,它能比较容易地提供永久、强大的覆盖面.(即:不需要时的时候不需要从其他系统运行如t()的代码)
使用t()函数的情况
在如下情况中需要用到t()函数:
- 当需要特别测试转义和定位功能时;
- 当测试用户界面中已经解释的字符串。如:当验证一个403页面”Access denied”信息是,采用如下代码
<?php
$this->assertText(t('Access denied'), 'Access denied message found on forbidden foo page.');
?>
注意:第一个字符串”Access denied”,由drupal_deliver_html_page()提供,用t()转义。因此,我们也用这个t()函数来检查这条消息是否存在。
不使用t()函数的情况:
不要在如下情况中使用t():
- getInfo()中含字符串.
- 测试语句消息,重复如上例子
<?php
$this->assertText(t('Access denied'), 'Access denied message found on forbidden foo page.');
?>
第二个字符串’Access denied message found on forbidden foo page’仅是注释信息,不要将它转义.
- 除非测试模块需要测试转义功能,否则对于测试模块和主题输出不要使用。
在如上例子中,如果需要设置格式或定制字符串输出,可以用format_string()函数
Simpletests调试
在测试设置中(D6: 管理/构建/测试/设置admin/build/testing/settings, D7: 管理/配置/开发/设置admin/config/development/testing/settings),有一个选项“Provide verbose information when running tests ".如果打开这个选项,则每个drupalGet()和drupalPost()函数将作为HTML文件被捕捉,以便让你浏览结果。这是个及其重要的工具。
你也可调用$this-> verbose(“Some message”)函数,如果verbose消息开关打开的话,函数中包含的消息就会显示出来。
进一步指南
注: Google Docs presentation to go with this material 由rfay维护。