跳转到主要内容
gokookie 提交于 30 April 2012

原文链接http://drupal.org/simpletest-tutorial-drupal7

注:本指南中引用代码来自Examples for Developers module,你可以:

  • 从该模块中复制一份代码,编辑、修改然后进行测试.
  • 报告所发现问题,进行修复,欢迎进行补丁修正和提出改进意见。

通过本指南,可以让你掌握基本的测试方法。学习本节后你可以建立第一个测试!在本例中,我们将创建一个叫“simplest example”的dummy 模块,它提供一个叫”simplest_example” 的内容类型。 这个内容类型和任何基本drupal节点类型(如:’page’)一样。本指南将阐述如何对这个simplest_example内容类型进行测试,从而保证其功能的有效实现。

准备工作

首先,我们要确保simpletest模块已安装。在Drupal7中,simpletest已是内核的一部分称为测试模块。如果你还没有安装该模块,确保Simpletest模块已经启用。

在Drupal7中,Simpletest Verbose选项默认是打开的。当然如果想确保它是否是打开的,,则可以去管理/配置/开发/设置(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 节点类型(Content > Add new content > Simpletest Example Node Type ),你会看到如下页面。

查看界面并选定要测试的内容。

仔细浏览确保你已经熟知接口间是如何工作的,在一个基本实例,而不是Simpletest操作中,功能可以实现。如果你还没有理解原理,是不能写好测试代码的。在本例中,我们来填上标题和正文内容,然后按保存按钮。你将看到如下页面。

 对simpletest_example构建测试

好,我们可以来测试了,我们会在simpletest_example.test文件中操作。(该文件可从下载的模块中获得)

在drupal7中,如果对一个模块添加了一个新的.test文件,则需要在该模块的.info文件将它添加到files[]部分。在我们的例子中,simpletest_example.info文件中在files[]下,就有simpletest_example.test 的记录.

files[] = simpletest_example.test

如果.file文件添加在现有的模块中,则可能需要重刷cache,让drupal知道有新文件产生了。要刷新cache,可以去admin/config/development/performance下点击“clear all chaches”按钮。

进行一个测试需要如下四个步骤:

  • 创建结构(即产生一个从DrupalWebTestCase继承的类)
  • 对任何用户创建或需要的配置的测试实例初始化
  • 用测试实例产生真实测试
  • 当然,要特别想清楚为什么我们的测试没有按预想地进行并调试测试(也许还有模块).

在开始前,我们需要模块扩展DrupalWebTestCase.

<?php /** * Tests the functionality of the Simpletest example content type. */ class SimpletestExampleTestCase extends DrupalWebTestCase { protected $privileged_user; } ?>

为了使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 content', 'extra special edit any simpletest_example', )); $this->drupalLogin($this->privileged_user); } ?>

注:在drupal6中,我们必须启用所有关联模块。Drupal7会自动启用有关联的模块.

测试建立:创建节点

现在让我们来创建测试实例练习模块。刚才我们已经为我们的测试类建立了成员函数,每一个成员我们会来练习一个测试。

我们的第一个测试将通过node/add/simpletest-example中的form来创建一个新的simpletest_example节点.

<?php /** * Tests creation of a Simpletest example node. */ public function testSimpleTestExampleCreate() { // Create node to edit. $edit = array(); $edit['title'] = $this->randomName(8); $edit["body[und][0][value]"] = $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).当超越本指南知识后,你会想参阅更多的语句

测试中的用户和内核环境

测试运行在一个独立的(沙盒)环境中。在测试环境中,用$this->drupalLogin($account)来指定一个用户登陆。像drupalGet和drupalPost的测试方法经常在沙盒中用到。如果要调用内核API函数(如:user_access), 强烈建议将这个测试用户授权于内核环境中。

<?php $account = $this->drupalCreateUser(array('access content')); $this->drupalLogin($account); global $user; $user = user_load($account->uid); $this->assertFalse(user_access('access content')); ?>

在测试中,Simpletest将用户转换成uid 1,并负责恢复用户,因此我们可以切换用户而不必考虑安全因素。

通过Web界面运行Simpletest

下面我们来运行测试。这里我们将用web界面来运行。

在模块页面,找到你创建的模块,然后进入配置页面。在打开的页面,你会看到有两个标签:配置(Settings)和列表(List)。在列表标签下,你会看到可选择的测试列表。选择你要创建的测试-它会归入”Examples”组—点击并运行”Runtests”(有时可能需要清除Drupal缓存才能在列表中见到)

测试运行后你就能见到是否通过(pass)的结果。

你也可以通过命令行运行。更多的内容请参阅通过命令行来运行测试

一个失败测试的演示

测试成功的例子没有必要多讲了,现在我们来举一个失败的例子。

还是用相同的类,这次来测试编辑节点的情况。我们的模块有一个缺陷-它不能很好的处理”edit own simpletest_exple”权限,当用户仅有此权限时缺不能编辑它。在本实例中我们将创建一个节点,然后来进行编辑。

运行测试并查看结果。

<?php /** * Tests editing a Simpletest example node. */ public function testSimpleTestExampleEdit() { $settings = array( 'type' => 'simpletest_example', 'title' => $this->randomName(32), 'body' => array(LANGUAGE_NONE => array(array($this->randomName(64)))), ); $node = $this->drupalCreateNode($settings); // For debugging, we might output the node structure with $this->verbose() $this->verbose('Node created: ' . var_export($node, TRUE)); // It would only be output if the testing settings had 'verbose' set. // We'll run this test normally, but not on the testbot, as it would // indicate that the examples module was failing tests. if (!$this->runningOnTestbot()) { // The debug() statement will output information into the test results. // It can also be used in Drupal 7 anywhere in code and will come out // as a drupal_set_message(). debug('We are not running on the PIFR testing server, so will go ahead and catch the failure.'); $this->drupalGet("node/{$node->nid}/edit"); // Make sure we don't get a 401 unauthorized response: $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(), 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调试

在测试设置中(admin/config/development/testing/settings),有一个选项“Provide verbose information when running tests ".如果打开这个选项,则每个drupalGet()和drupalPost()函数将作为HTML文件被捕捉,以便让你浏览结果。这是个及其重要的工具。

你也可调用$this-> verbose(“Some message”)函数,如果verbose消息开关打开的话,函数中包含的消息就会显示出来。

更多的debug()$this->verbose()信息可以参考 Boombatower's blog about debugging in Drupal 7

进一步指南

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

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