利用单一来源的 DITA 创建演练脚本和验收脚本

在整个开发周期中受益于文档重用

配以一款有效的 XML 编辑器,达尔文信息分类体系结构(Darwin Information Typing Architecture,DITA)为开发基于主题的用户文档(用于描述如何使用您的应用程序)提供一个有用的工具。事先稍加预筹和规划,您可以将这些相同的主题重新应用于开发过程中更容易发挥价值的文档中,比如用于客户演示的演练脚本(walk-through script)或者用于手动质量保证意图的验收脚本(acceptance script)。

Piers Michael Hollott, 高级顾问, Sierra Systems

Piers Hollott 从事软件行业工作已经 15 年了,他专长于 Java 开发、XML 技术和函数式编程。他为好几个开源项目贡献过力量,目前是 Sierra Systems 的一名顾问。



2010 年 11 月 22 日

随着软件开发项目和开发团队的增长,您可能需要创建参考文档,比如供内部或外部使用的用户指南。越往后拖延,创建这类文档就会变得越麻烦。开发一个支持从单一来源创建多种类型文档的框架对于小型和大型项目都是有利的;再辅以前瞻考虑,您还可以利用该文档来支持您的项目的质量保证(QA)和测试需求。本文展示 DITA 和 XSLT 2.0 转换可以如何促进针对开发和用户文档的单一来源策略。

常用缩略词

  • DBMS:数据库管理系统
  • DTD:文档类型定义
  • HTML:超文本标记语言
  • OASIS:结构化信息标准促进组织
  • PDF:便携文档格式
  • RSS:真正简单联合发布系统
  • UI:用户界面
  • XML:可扩展标记语言
  • XSL:可扩展样式表语言
  • XSLT:可扩展样式表语言转换

本文附带提供的 XSL 转换(参见 下载)是我五年前开发的一个开源项目的扩展,那时我旨在缩短文档化和 QA 时间。我是想要简化收集用例和将之转换成客户端验收测试等任务,这些测试然后被重用为新开发人员的培训资源。在某些方面,此项目反映出业务智能的薄弱;我扮演了 QA、技术作者和开发领头人的角色。换个角度,我将这个作品看作敏捷策略的早期标志,在后续项目中我发现敏捷策略是不可或缺的 — 比如说可以促进客户端演练、与用例松散耦合以及只完成足够的操作就继续前进。

随着项目的增长,我最终得到一个驻留在 XML DBMS 中的大型 XML 文件。它包含对正处于开发中的软件的高级应用程序体系结构的描述,该体系结构被分解成模块、页面、页面状态和页面上的控件。与这个文件一起,我还构建了一个 XQuery 和 XSL 转换集合,可以基于前两周中发生的改变从中提取和发布用户指南、验收测试文档和 RSS 提要,此提要包含应用程序的导航路径。RSS 提要推动了手动测试工作。

那时,我尽管听说过 DITA,但是没有实际体验过,只知道它是一个相当新的规范。所以,我创建了自己的模式,开始收集数据,并开发了我的转换。我对 DITA 的了解无疑影响了我采纳的方法,因为我:

  • 利用了模块化和重用
  • 编写了一个基本的主题结构
  • 创建了概念集合和任务列表

从 DITA 主题开始:概念、任务和主题图

本文中描述的样例应用程序难以置信地简单。它有一个首页,还有一个三页的向导,可以用来输入一个人的姓名和住址。此应用程序并不实际存在 — 就是说,只存在于理论中。它包含单个路径(尽管在未来的版本中我想要扩展样例文件以包含多个备选的流)。

用来创建用户指南的数据被分成页面和用例,其中每个页面表示单个 HTML 页面,用例表示单个用例,比如 “在名字域输入您的名字,并验证它的大小写是否正确”。这些主题被收集到 DITA 主题图中,您可以使用 DITA Open Toolkit 通过 DITA 主题图产生一个包含逐个页面用户指南的 PDF 文件。

基于主题写作的 DITA 方法将重点放在以下三个基本的主题类型上:

  • 概念,它有些抽象
  • 引用,它更为具体
  • 任务,它描述完成某件事所采取的步骤

清单 1 展示了一个样例概念;清单 2 展示了一个样例任务。一旦有了很多已写好的主题,您就可以使用一个主题图(类似于目录)将它们收集并组织到一个文档中。可以对相同主题使用多个主题图来产生多个文档。

样例应用程序中的每个页面用一个 DITA 概念来表示,还有页面用途的描述、页面上导航控件的集合以及对适合于此页面的任何用例的引用。在 清单 1 中可以看到描述一个页面的 DITA 概念如何包含页面上 UI 元素的描述和对单个用例任务的引用。

清单 1. 一个应用程序页面的 DITA 概念
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN"
 "../../dtd/concept.dtd">
<concept id="wiz2" xml:lang="en-us">
    <title>Wizard Page 2</title>
    <conbody>
        <p>Name Details</p>
        <section>
            <title>User Interface: Controls</title>
            <screen>
                <uicontrol id="wiz3">Continue</uicontrol>
                <uicontrol importance="optional" 
                        id="cancel">Cancel</uicontrol>
            </screen>
        </section>
        <section>
            <title>Use Cases</title>
            <xref href="../cases/case_004.dita" type="task"/>
        </section>
    </conbody>
</concept>

元素 screenuicontrol 在 DITA 文档中通常用于描述 UI 组件。本文中它们也用于此目的,但是您马上会看到,这些元素也计算通过样例应用程序的导航路径。

用例用 DITA 任务表示,该任务确定产生某个结果所采取的一组具体的步骤。采用这种方法文档化一个实际的应用程序时,我也添加了针对已知问题的用例,以关键字 regression 做标记,并按照用户指南执行它们。主题图被重新用作测试文档时,可以包含这些回归测试,从而为手动测试提供一个上下文。

清单 2 展示了一个 DITA 任务,其中详细列出了重新产生一个可在页面上执行的操作所必需的步骤。注意其中一个步骤 — 添加中间名字 — 是如何被标记为可选的。

清单 2. 一个应用程序用例的 DITA 任务
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN"
 "../../dtd/task.dtd">
<task id="case_004" xml:lang="en-us">
    <title>Case 004</title>
    <prolog></prolog>
    <taskbody>
        <context><p>context: case 004</p></context>
        <steps>
            <step><cmd>Enter First Name</cmd></step>
            <step><cmd>Enter Last Name</cmd></step>
            <step importance="optional"><cmd>Enter Middle Name</cmd></step>
        </steps>
    </taskbody>
</task>

样例 DITA 项目的文件夹结构非常直截了当,带有用例和页面的目录。稍后,这些目录将会伴有一个用于测试的目录。此外,还为用于产生测试主题的 XSL 转换维护着一个目录。DITA 主题图放在项目的根目录中。

这个简单应用程序的 DITA 主题图也很简单,参见 清单 3。使用 DITA Open Toolkit 生成用户指南时,结果很直观,也很有用,您可以容易地向单个页面文件添加信息,以增加复杂度和深度。下面您可以看到主题图是多么类似于一个包含四部分的目录表。

清单 3. 一个 DITA 主题图
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN"
 "../dtd/map.dtd">
<map title="Sample Application User Guide">
    <topicref href="pages/home.dita" type="concept" id="home"/>
    <topicref href="pages/wiz1.dita" type="concept"/>
    <topicref href="pages/wiz2.dita" type="concept"/>
    <topicref href="pages/wiz3.dita" type="concept"/>
</map>

将主题转换成演练文档

由于使用了 DITA screenuicontrol 元素来描述页面上的导航控件(链接、按钮和菜单),所以从单个 DITA 页面文件提取信息并使用此信息创建一组新的 DITA 任务并不是什么困难的事情。这些任务描述如何从一个页面导航到另一个页面 — 例如 清单 4 中的导航任务 nav_home_wiz1.dita。您可以看到命名约定如何连接两个页面的标识符,来表示从一个页面到另一个页面的导航;来自初始页面概念的信息现在表示一个导航任务。

清单 4. 一个导航任务
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE task PUBLIC "-//OASIS//DTD DITA Task//EN" "../dtd/task.dtd">
<task id="nav_home_wiz1.dita" xml:lang="en-us">
    <title>Navigate to Wizard Page 1</title>
    <prolog/>
    <taskbody>
        <steps>
            <step>
                <cmd>On Home Page, click 
                    <uicontrol>Launch Wizard</uicontrol>
                </cmd>
            </step>
            <step>
                <cmd>Verify that Wizard Page 1 loads</cmd>
            </step>
        </steps>
    </taskbody>
</task>

导航任务放在与页面目录和用例目录一起的一个叫做 tests 的新目录中。除了这些任务之外,测试还包含一组新的测试概念 — 每个页面一个— 它们只是识别正在测试的页面。您可以扩展这些概念(就像 清单 5 中的向导页面测试概念一样)以包含关于如何测试页面的更多信息 — 例如,识别哪些页面适用于哪些用户角色、优先级和测试严格度,等等。

清单 5. 一个测试概念
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE concept PUBLIC "-//OASIS//DTD DITA Concept//EN" "../dtd/concept.dtd">
<concept id="test_wiz1.dita" xml:lang="en-us">
    <title>Wizard Page 1</title>
    <conbody>
        <p>This is the first wizard page: Name Details</p>
    </conbody>
</concept>

每个测试概念文档中包含的信息直接来自于页面目录中的 DITA 概念。实际上,包含在测试目录中的所有主题都使用 XSLT 直接产生于另外两个目录和用户指南主题图(参见 清单 3)中的主题。清单 6 展示了一个新的演练主题图,它也产生于这些相同的文件。您将演练主题图作为一个参数提供给 DITA Open Toolkit 中的构建脚本时,此工具包会使用新的任务和概念来产生演练脚本,用于遍历样例应用程序的导航图。结果是一个通过应用程序的路径,指导应用程序的一个结构化演练,用于内部审计或供客户端使用。

对于每个页面,演练脚本也包含从用例总结而来的验收标准。至于用例,您可以在客户演示期间将其用作论据,或者由手工测试人员在用户或工厂验收测试过程中使用。由于您可以在另外两个目录中的内容发生改变时重新产生整个测试目录,所以您可以在开发周期的任何阶段重新产生并使用该文档,无论您是有单个页面,还是有数百个页面。

清单 6. DITA 演练主题图
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE map PUBLIC "-//OASIS//DTD DITA Map//EN" "../dtd/map.dtd">
<map title="Sample Application User Guide">
    <topicref href="tests/test_home.dita" type="concept">
        <topicref href="pages/../cases/case_001.dita" type="task"/>
        <topicref href="pages/../cases/case_002.dita" type="task"/>
        </topicref>
    <topicref href="tests/test_wiz1.dita" type="concept">
        <topicref href="tests/nav_home_wiz1.dita" type="task"/>
            <topicref href="pages/../cases/case_003.dita" type="task"/>
        </topicref>
    <topicref href="tests/test_wiz2.dita" type="concept">
        <topicref href="tests/nav_wiz1_wiz2.dita" type="task"/>
            <topicref href="pages/../cases/case_004.dita" type="task"/>
        </topicref>
    <topicref href="tests/test_wiz3.dita" type="concept">
        <topicref href="tests/nav_wiz2_wiz3.dita" type="task"/>
        <topicref href="pages/../cases/case_005.dita" type="task"/>
        <topicref href="pages/../cases/case_006.dita" type="task"/>
    </topicref>
    <topicref href="tests/test_home.dita" type="concept">
        <topicref href="tests/nav_wiz3_home.dita" type="task"/>
    </topicref>
</map>

通过 XSLT 2.0 进行精简

首次开发一个模式来建模用于创建和重复利用这些文档的信息时,我努力避免在前一个项目中遇到的问题。以前,我开发了太细粒度的模式;而现在则相反,我的文件太庞大,我所有的 XML 都放在单个文件中。当然,这种思路也确实有好几个优点,比如减少了文件管理和版本方面的问题,同时也降低了产生新信息所必需的转换复杂度。尽管通过使用 ditabase 元素(它主要充当诸如 concepttaskreference 之类 DITA 主题的容器),庞大的 DITA 方法是可行的,但是 DITA 社团中常见的最佳实践是为每个文件创建一个 DITA 主题。较细粒度的方法(比如说这里采用的)支持比较高级的版本控制,并能促进重用和重定目标,后者正是这里谈论的主题。

使之尽可能细粒度(我就是这么做的)更为高效。对于 XSL 转换本身来说,增加的粒度带来两个主要问题:从多个文件拖出信息以及将信息放回多个文件。为了从多个文件检索信息,我使用了 xsl:document() 函数,只要您是处理平板文件结构,这个函数很容易使用。为了从这些各不相同的 DITA 文档在测试目录中产生多个文档,我使用了 xsl:result-document 元素,这是 XSLT 2.0 中引入的元素。清单 7清单 8 演示了如何在 XSL 转换中使用 xsl:result-document 来创建新的概念和任务。

清单 7. 使用 xsl:result-document 产生 DITA 概念
<xsl:result-document href="..\{$doc-path-test}" 
    doctype-system="../dtd/concept.dtd" 
    doctype-public="-//OASIS//DTD DITA Concept//EN">
    <concept id="{$doc-name-test}" xml:lang="en-us">
        <title>
            <xsl:value-of select="title"/>
        </title>
        <conbody>
            <p>
                <xsl:value-of select="conbody/p"/>
            </p>
        </conbody>
    </concept>
</xsl:result-document>

注意:清单 7清单 8 中,对 xsl:output-encoding 可用的 doctype-systemdoctype-public 属性也对 xsl:result-document 元素可用。没有这些属性,就不可能向生成的文件附加适当的 DITA DTD;没有这些 DTD,您就不能在 DITA Open Toolkit 中使用生成的文件。因此,这些属性的使用是至关重要的。

另外,尽管使用 DITA tasks 来表示用例是非常有意义的,但是用 DITA references 而不是 concepts 来表示页面也是明智的,因为它们指向实际的东西,比如说 HTML 页面。我决定使用概念来表示页面,因为我知道,我要通过它们产生更抽象的测试概念,并且我想要保持这种一致。换句话说,这是一个主观的选择。

清单 8 中您可以看到,生成导航任务文档的 XSLT 比用来产生概念的 XSLT 要稍微复杂一些。

清单 8. 使用 xsl:result-document 产生 DITA 任务
<xsl:result-document href="..\{$doc-path-nav}" 
    doctype-system="../dtd/task.dtd" 
    doctype-public="-//OASIS//DTD DITA Task//EN">
    <task id="{$doc-name-nav}" xml:lang="en-us">
        <title>Navigate to <xsl:value-of select="title"/>
        </title>
        <prolog/>
        <taskbody>
            <steps>
                <step>
                    <cmd>On <xsl:value-of select="$source_title"/>,
                         click <xsl:copy-of select="$uicontrol"/>
                    </cmd>
                </step>
                <step>
                    <cmd>Verify that 
                        <xsl:value-of select="title"/> loads</cmd>
                </step>
            </steps>
        </taskbody>
    </task>
</xsl:result-document>

注意,这里使用了 doctype-systemdoctype-public 属性来向 XSL 转换创建的主题附加适当的 OASIS DITA DTD。参考 清单 4 中创建任务的例子。

新文档生成时,它们在生成它们的相同 XSL 转换中被引用,如 清单 9 所示。通过递归地遍历源页面概念中 uicontrol 元素包含的 id 属性,您可以使用这些引用从现有用户指南主题图创建新的演练主题图。通过 XSLT 2.0,我能够使用单个转换来创建主题图和所有的支持文件 — 一件使用 XSLT 1.0 不可能做到的事情(或者至少很困难)。

清单 9. 引用产生的测试概念和任务
<topicref href="{$doc-path-test}" type="concept">
    <topicref href="{$doc-path-nav}" type="task"/>
    <xsl:if test="@id != 'home' ">
        <xsl:apply-templates select="conbody" mode="cases"/>
    </xsl:if>
</topicref>

注意:要查看完整的 XSL 转换,请 下载 附带的样例文件。

每当改变用于产生这些主题的内容时,您就可以重新产生新的主题,以便演练文档或验收文档是及时的、最新的。例如,一个构建脚本可能会删除任何现有的已生成文件,运行遍历转换的用户指南,然后使用 DITA Open Toolkit 构建脚本产生用户指南和验收文档或演练文档的当前副本(作为 PDF 文件)。

由于 DITA screenuicontrol 元素描述导航控件(参见 清单 1),遍历一个通过应用程序的路径变成简单的递归。整个程序中我使用了 importance 属性(该属性对于 DITA 元素很常见)来表示可选的导航控件,比如 Cancel 按钮,对于导航来说,可以忽略此按钮。一个类似的策略用于防止可怕的堆栈溢出,当从一个页面导航回到前面已经遍历过的页面时,会发生堆栈溢出。我也发现 otherprops 属性对于此目的有用 — 同样,因为它很常见,并且很容易便可用于所有 DITA 元素。


展望未来

对于一个简单的应用程序,我已经描述了一个简单的转换。我希望,本文演示如何开发一种文档重用策略以及及早地初始化文档项目的一些优点,以便您可以马上以及在整个软件项目生命周期中将它们用于其他目的。使用单一来源产生横切开发周期各个阶段的多个文档可以带来进一步的机会利用这一共享知识。这种方法很重要,因为文档若被软件项目中的多个涉众使用,那么随着时间的推移,它很可能会得到维护和增值。

引用更灵活的文档方法也具有横切方法(crosscutting methodologies)的优点。如果您遇到一个客户,比如说他要求非常严格而死板,您的开发团队采用的却是灵活方法,那么重新利用文档可能正是您同时满足客户和所选方法的需求所需要的方法。如果属于这种情况,那么 DITA 和 XSLT 2.0 就是您需要的工具。


下载

描述名字大小
DITA 项目的源文件sample_code.zip44KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=XML
ArticleID=588178
ArticleTitle=利用单一来源的 DITA 创建演练脚本和验收脚本
publish-date=11222010