跳转到主要内容
gokookie 提交于 1 May 2012

原文链接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()函数。这里有两个原因:

  1. 许多simpletest的字符串仅仅是测试目的,不会用到网站上,因此不需要解释它们。
  2. 当不同组件尽可能分开时,它能比较容易地提供永久、强大的覆盖面.(即:不需要时的时候不需要从其他系统运行如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消息开关打开的话,函数中包含的消息就会显示出来。

进一步指南

  • 学习本章节叙述的几个API函数语句
  • 通读drupal_web_test_case.php是很有意义的

注: Google Docs presentation to go with this materialrfay维护。