﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-jinfeng_wang-随笔分类-OpenSource</title><link>http://www.blogjava.net/jinfeng_wang/category/22957.html</link><description>G-G-S,D-D-U!</description><language>zh-cn</language><lastBuildDate>Fri, 30 Nov 2007 07:42:18 GMT</lastBuildDate><pubDate>Fri, 30 Nov 2007 07:42:18 GMT</pubDate><ttl>60</ttl><item><title>使用 EclEmma 进行覆盖测试 zz</title><link>http://www.blogjava.net/jinfeng_wang/archive/2007/11/30/164254.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 30 Nov 2007 06:04:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2007/11/30/164254.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/164254.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2007/11/30/164254.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/164254.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/164254.html</trackback:ping><description><![CDATA[<blockquote>覆盖测试是衡量测试质量的一个重要指标。在对一个软件产品进行了单元测试、组装测试、集成测试以及接受测试等繁多的测试之后，我们能不能就此对软件的质量产生一定的信心呢？这就需要我们对测试的质量进行考察。如果测试仅覆盖了代码的一小部分，那么不管我们写了多少测试用例，我们也不能相信软件质量是有保证的。相反，如果测试覆盖到了软件的绝大部分代码，我们就能对软件的质量有一个合理的信心。本文将介绍一个优秀的开源软件测试工具 EclEmma，它能够对由 Java 语言编写的程序进行覆盖测试，从而对程序运行的结果生成详尽的覆盖测试报告。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p><a name="N10047"><span class="atitle">介绍</span></a></p>
<p>现在 IT 开发人员比以往任何时候都更加关注测试的重要性，没有经过良好测试的代码更容易出问题。在极限编程中，测试驱动开发已经被证明是一种有效提高软件质量的方法。在测试驱动的开发方式中，软件工程师在编写功能代码之前首先编写测试代码，这样能从最开始保证程序代码的正确性，并且能够在程序的每次演进时进行自动的回归测试。测试对于软件产品的成败起着至关重要的作用，在极限编程领域，甚至有人提议任何未经测试的代码都应该自动从发布的产品中删除。作者并不确信这个观点是正确的，但是测试本身的质量确实是一个需要高度关注的问题。测试的覆盖率是测试质量的一个重要指标，我们需要工具来帮助我们进行对软件测试覆盖的考察。</p>
<p>EclEmma 就是这样一个能帮助开发人员考察测试覆盖率的优秀的 Eclipse 开源插件。EclEmma 在覆盖测试领域是如此的优秀，以致于它在过去不久的 2006 年成为了 Eclipse Community Awards Winners 决赛选手。虽然最后 Eclipse Checkstyle Plugin 取得了 Best Open Source Eclipse-based Developer tool 的称号，但我们也可以由此看到 EclEmma 对开发人员的帮助是巨大的（Eclipse Community Award 的具体信息可以参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#resources">参考资源</a>）。</p>
<p>提到 EclEmma 首先就要说到著名的 Java 覆盖测试工具 Emma。Emma 是一个在 SourceForge 上进行的开源项目（参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#resources">参考资源</a>）。从某种程度上说，EclEmma 可以看作是 Emma 的一个图形界面。在本文的<a href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#resources">参考文献</a>中，可以看到专门讲述使用 Emma 的技术文章。</p>
<p>Emma 的作者开发 Emma 之初，程序员已经有了各种各样优秀的开源 Java 开发工具。举例来说，我们有优秀的集成开发环境 Eclipse，有开源的 JDK，有单元测试工具 JUnit，有 Ant 这样的项目管理工具，我们还可以用 CVS 或 SubVersion 来进行源代码版本的维护。当时看来，也许唯一缺少的就是一个开源的覆盖测试工具了。Emma 就是为了填补这项空白而生的。现在的情况已经和 Emma 诞生的时候不一样的。时至今日，我们已经有了不少的覆盖测试工具。例如 Coverlipse 是一个基于 Eclipse 的覆盖测试插件。其他还有 Cobertura，Quilt 和 JCoverage 等。但是 Emma 具有一些非常优秀的特性使得它更适合被广泛的使用。和 Coverlipse 等工具比起来，Emma 是开源的，同时它对应用程序执行速度的影响非常小。</p>
<p>EclEmma 的出现弥补了 Emma 用户一个大的遗憾 ---- 缺乏图形界面以及对集成开发环境的支持。将 Eclipse 和 Emma 这两个在各自领域最为优秀的工具结合起来，这就是 EclEmma 为我们提供的。接下来，我们就要在后续章节中和读者朋友一起看看 EclEmma 为开发人员提供了什么。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10068"><span class="atitle">安装 EclEmma 插件</span></a></p>
<p>安装 EclEmma 插件的过程和大部分 Eclipse 插件相同，我们既可以通过 Eclipse 标准的 Update 机制来远程安装 EclEmma 插件（<a href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#fig001">图 1</a>），也可以从站点（参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#resources">参考资源</a>）下载 zip 文件并解压到 eclipse 所在的目录中。</p>
<br />
<a name="fig001"><strong>图 1 添加 EclEmma 更新站点</strong></a><br />
<img alt="图 1  添加 EclEmma 更新站点" src="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/fig001.jpg" /> <br />
<p>不管采用何种方式来安装 EclEmma，安装完成并重新启动 Eclipse 之后，工具栏上应该出现一个新的按钮：</p>
<br />
<a name="fig002"><strong>图 2 新增的覆盖测试按钮</strong></a><br />
<img alt="图 2  新增的覆盖测试按钮" src="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/fig002.jpg" /> <br />
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10094"><span class="atitle">使用 EclEmma 测试 Java 程序</span></a></p>
<p>为了实验 EclEmma 的特性，我们首先在 Eclipse 的 Workspace 中建立一个名称为 test.emma 的新 Java 项目。接下来，我们在其中建立一个 <code>HelloWorld</code> 类，其代码如下所示：</p>
<br />
<a name="N100A4"><strong>清单 1 用于测试 EclEmma 的代码</strong></a><br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            package test.emma;
            public class HelloWorld {
            /**
            * @param args
            */
            public static void main(String[] args) {
            int rand = (int) (Math.random()*100);
            if(rand%2==0){
            System.out.println( "Hello, world! 0");
            }
            else
            System.out.println("Hello, world! 1");
            int result = rand%2==0? rand+rand:rand*rand;
            System.out.println(result);
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>接下来，我们通过 EclEmma 运行 <code>HelloWorld.main()</code> 函数。</p>
<br />
<a name="fig003"><strong>图 3 对 Java 应用程序进行覆盖测试</strong></a><br />
<img alt="图 3  对 Java 应用程序进行覆盖测试" src="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/fig003.jpg" /> <br />
<p>执行完毕之后，我们正在编辑 HelloWorld.java 的窗口将会变成如下所示：</p>
<br />
<a name="fig004"><strong>图 4 进行覆盖测试的结果</strong></a><br />
<img alt="图 4  进行覆盖测试的结果" src="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/fig004.jpg" /> <br />
<p>在 Java 编辑器中，EclEmma 用不同的色彩标示了源代码的测试情况。其中，绿色的行表示该行代码被完整的执行，红色部分表示该行代码根本没有被执行，而黄色的行表明该行代码部分被执行。黄色的行通常出现在单行代码包含分支的情况，例如 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#fig004">图 4</a> 中的 16 行就显示为黄色。由于程序中有一个随机确定的分支，因此读者的窗口可能与这里稍有不同（11 行或者 14 行中有且只有一个红色的行）。</p>
<p>除了在源代码编辑窗口直接进行着色之外，EclEmma 还提供了一个单独的视图来统计程序的覆盖测试率。</p>
<br />
<a name="fig005"><strong>图 5 察看程序的覆盖测试率</strong></a><br />
<img alt="图 5  察看程序的覆盖测试率" src="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/fig005.jpg" /> <br />
<p>EclEmma 提供的 Coverage 视图能够分层的显示代码的覆盖测试率，<a href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#fig005">图 5</a> 中的信息表明我们对 HelloWorld 的一次运行覆盖了大约 68.6% 的代码。</p>
<p>想在一次运行中覆盖所有的代码通常比较困难，如果能把多次测试的覆盖数据综合起来进行察看，那么我们就能更方便的掌握多次测试的测试效果。EclEmma 提供了这样的功能。现在，让我们重复数次对 HelloWorld 的覆盖测试。我们注意到 Coverage 视图总是显示最新完成的一次覆盖测试。事实上，EclEmma 为我们保存了所有的测试结果。接下来，我们将通过 Coverage 视图的工具按钮来结合多次覆盖测试的结果。</p>
<br />
<a name="fig006"><strong>图 6 用于结合多次覆盖测试结果的工具栏按钮</strong></a><br />
<img alt="图 6  用于结合多次覆盖测试结果的工具栏按钮" src="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/fig006.jpg" /> <br />
<p>当我们多次运行 Coverage 之后，我们可以单击 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#fig006">图 6</a> 所示工具栏按钮。之后，一个对话框将被弹出以供用户选择需要合并的覆盖测试。</p>
<br />
<a name="fig007"><strong>图 7 选择需要合并的覆盖测试结果</strong></a><br />
<img alt="图 7  选择需要合并的覆盖测试结果" src="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/fig007.jpg" /> <br />
<p>在合并完成之后，我们可以观察到 Java 编辑器和 Coverage 视图中都显示了合并之后的结果：</p>
<br />
<a name="fig008"><strong>图 8 察看合并后的覆盖测试结果</strong></a><br />
<img alt="图 8  察看合并后的覆盖测试结果" src="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/fig008.jpg" /> <br />
<p><a href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#fig008">图 8</a> 中，我们可以看到，通过多次运行覆盖测试，最终我们的代码达到了 91.4% 的测试覆盖率。有趣的是，图中第三行代码被标记为红色，而此行代码实际上是不可执行的。奥妙在于，我们没有生成任何 HelloWorld 类的实例，因此缺省构造函数没有被调用，而 EclEmma 将这个特殊代码的覆盖状态标记在类声明的第一行。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10121"><span class="atitle">EclEmma 的高级特性</span></a></p>
<p>如果 EclEmma 只能测试 Java Application 的测试覆盖率，那么它相对命令行版本的 Emma 来说，提供的增强就不多了。相反，EclEmma 提供了很多与 Eclipse 紧密结合的功能。它不仅能测试 Java Application，还能计算 JUnit 单元测试，对 Eclipse 插件测试的覆盖率。从 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#fig009">图 9</a> 中我们可以看到 EclEmma 目前支持四种类型的程序。</p>
<br />
<a name="fig009"><strong>图 9 EclEmma 的配置页面</strong></a><br />
<img alt="图 9  EclEmma 的配置页面" src="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/fig009.jpg" /> <br />
<p>为了了解 EclEmma 是如何获得覆盖测试数据的，我们需要先对 Emma 有初步的了解。通常代码覆盖测试工具都需要对被执行的代码进行修改。而 Emma 提供了两种方式来完成这件事。</p>
<ol>
    <li><strong>预插入模式：</strong>对程序进行测量之前，需要采用 Emma 提供的工具对 class 文件或者 jar 文件进行修改。修改完成之后的代码可以立刻被执行。覆盖测试的结果将会被存放到指定的文件中。
    <li><strong>即时插入模式：</strong>即时插入模式不需要事先对代码进行修改。相反，对代码的修改是通过一个 Emma 定制的 Class loader（类载入器）进行的。这种方式的优点很明显，我们不需要对 class 或者 jar 文件进行任何修改。缺点是我们为了获得测试的结果，需要用 Emma 提供的命令 emmarun 来执行 Java 应用程序。 </li>
</ol>
<p>使用即时插入模式的优点很明显：class 文件和 jar 文件不会被修改。而预插入模式的应用范围更为广泛，对于某些需要嵌入到框架中运行的代码来说（例如 EJB），我们只能使用预插入模式。EclEmma 仅仅使用了 Emma 的预插入模式来工作，不过 EclEmma 缺省会在临时目录中创建 class 文件和 jar 文件的副本来进行修改，因此在 workspace 中 class 和 jar 文件仍然保持原样。虽然听上去很好，但是由于需要修改 classpath 来使用修改过的 class 和 jar 文件，对于不能修改 classpath 的应用（例如 Eclipse RCP 和 JUnit Plugin Test）来说，我们还是只能选择修改 workspace 中的 class 文件和 jar 文件。对于 Java Application 和 JUnit 类型的覆盖测试，我们可以在配置对话框中选中<strong>&#8220;In-place instrumentation&#8221;</strong>项来指定直接修改 Workspace 中的 .class 文件和 .jar 文件。</p>
<br />
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
            <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
            <table cellspacing="0" cellpadding="0" border="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/java/j-lo-eclemma/index.html#main"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10152"><span class="atitle">结论</span></a></p>
<p>本文通过一个简单的例子介绍了使用 EclEmma 进行覆盖测试的基本过程。EclEmma 允许软件工程师方便的考察测试的覆盖率，并能将测试结果以直观、简洁的方式展现给开发人员。</p>
<br />
<br />
<p><a name="resources"><span class="atitle">参考资料 </span></a></p>
<strong>学习</strong><br />
<ul>
    <li>developerWorks 文章&#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-lo-emma/">使用 EMMA 测量测试覆盖率</a>&#8221;：介绍了 EclEmma 的核心 Emma 的使用方法。<br />
    <br />
    <li>developerWorks 文章&#8220;<a href="http://www.ibm.com/developerworks/cn/java/l-junit/">怎样使用 JUnit Framework 进行单元测试的编写</a>&#8221;：一篇介绍 JUnit 的优秀文章。<br />
    <br />
    <li>developerWorks 文章&#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-testng/">TestNG 使 Java 单元测试轻而易举</a>&#8221;：介绍了一个比 JUnit 更新的测试框架 TestNG。<br />
    <br />
    <li>developerWorks 文章&#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-cq01316/index.html">追求代码质量: 不要被覆盖报告所迷惑</a>&#8221;：覆盖测试也有其局限性，通过本文可以理解即使达到了 100% 的覆盖率，bug 也仍然可能出现。<br />
    <br />
    <li>developerWorks 文章&#8220;<a href="http://www.ibm.com/developerworks/cn/rational/r-ct/">持续测试: 将错误扼杀在摇篮之中</a>&#8221;：介绍了一种更高效的进行单元测试的有趣技术。<br />
    <br />
    </li>
</ul>
<br />
<strong>获得产品和技术</strong><br />
<ul>
    <li><a href="http://sourceforge.net/projects/eclemma/">下载 </a>EclEmma 安装文件。<br />
    <br />
    <li>访问 <a href="http://emma.sourceforge.net/">Emma</a> 的官方站点。</li>
</ul>
<br />
<strong>讨论</strong><br />
<ul>
    <li>通过 <a href="http://www.testdriven.com/">测试驱动开发社区</a>，您可以了解更多关于测试驱动开发的信息。<br />
    <br />
    <li>了解关于 <a href="http://www.eclipse.org/org/press-release/20070306eclipsecommunityawards.php">Eclipse Community Award</a> 的具体信息。<br />
    <br />
    </li>
</ul>
<br />
<br />
<p><a name="author"><span class="atitle">关于作者</span></a></p>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr>
            <td colspan="3"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /></td>
        </tr>
        <tr valign="top" align="left">
            <td>
            <p>&nbsp;</p>
            </td>
            <td><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="4" /></td>
            <td width="100%">
            <p>甘志，IBM 中国软件实验室（CSDL BJ）China Emerging Technology Institute 成员，主要研究方向为 UML、Model driven development 和 SOA。他在上海交通大学计算机系获得网络安全方向博士学位，期间发表了多篇论文和技术书籍。你可以通过 <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#103;&#97;&#110;&#122;&#104;&#105;&#64;&#99;&#110;&#46;&#105;&#98;&#109;&#46;&#99;&#111;&#109;">ganzhi@cn.ibm.com</a> 联系他。</p>
            </td>
        </tr>
    </tbody>
</table>
<br />
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/164254.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2007-11-30 14:04 <a href="http://www.blogjava.net/jinfeng_wang/archive/2007/11/30/164254.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>便利的开发文档工具-doxygen</title><link>http://www.blogjava.net/jinfeng_wang/archive/2007/06/01/121339.html</link><dc:creator>jinfeng_wang</dc:creator><author>jinfeng_wang</author><pubDate>Fri, 01 Jun 2007 05:17:00 GMT</pubDate><guid>http://www.blogjava.net/jinfeng_wang/archive/2007/06/01/121339.html</guid><wfw:comment>http://www.blogjava.net/jinfeng_wang/comments/121339.html</wfw:comment><comments>http://www.blogjava.net/jinfeng_wang/archive/2007/06/01/121339.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/jinfeng_wang/comments/commentRss/121339.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/jinfeng_wang/services/trackbacks/121339.html</trackback:ping><description><![CDATA[<p><a href="http://www.moon-soft.com/doc/39077.htm">http://www.moon-soft.com/doc/39077.htm</a><br><br><br>便利的开发文档工具-doxygen<br>mounton @ {www.ihere.org} ( <a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#109;&#111;&#117;&#110;&#116;&#48;&#110;&#64;&#121;&#97;&#104;&#111;&#111;&#46;&#99;&#111;&#109;"><u><font color=#0000ff>mount0n@yahoo.com</font></u></a>)<br>2003年8月</p>
<p>0. 序言<br>为代码写注释一直是大多数程序员有些困扰的事情。当前程序员都能接受为了程序的可维护性、可读性编码的同时写注释的说法，但对哪些地方应该写注释，注释如何写，写多少等这些问题，很多程序员仍然没有答案。更头痛的是写文档，以及维护文档的问题，开发人员通常可以忍受编写或者改动代码时编写或者修改对应的注释，但之后需要修正相应的文档却比较困难。如果能从注释直接转化成文档，对开发人员无疑是一种福音。而doxygen就能把遵守某种格式的注释自动转化为对应的文档。</p>
<p>Doxygen是基于GPL的开源项目，是一个非常优秀的文档系统，当前支持在大多数unix（包括linux），windows家族，Mac系统上运行，完全支持C++, C, Java, IDL（Corba和Microsoft 家族）语言，部分支持PHP和C#语言，输出格式包括HTML、latex、RTF、ps、PDF、压缩的HTML和unix manpage。有很多开源项目（包括前两篇文章介绍的log4cpp和CppUnit）都使用了doxygen文档系统。而国内的开发人员却使用的不多，这里从开发人员使用的角度介绍这个工具，使开发人员用最少的代价尽快掌握这种技术，并结合这个工具探讨如何撰写注释的问题。以下以linux下的C++语言为例进行介绍，以下讨论基于doxygen1.3.3。</p>
<p>1. doxygen使用步骤<br>由于只是工具的使用，这里不介绍它的原理，直接从使用步骤开始。Doxygen的使用步骤非常简单。主要可以分为：<br>&nbsp;1）第一次使用需要安装doxygen的程序<br>&nbsp;2）生成doxygen配置文件<br>&nbsp;3）编码时，按照某种格式编写注释<br>&nbsp;4）生成对应文档<br>doxygen的安装非常简单， linux下可以直接下载安装包运行即可，下载源代码编译安装也是比较通用的编译安装命令。请参考其安装文档完成安装。</p>
<p>Doxygen在生成文档时可以定义项目属性以及文档生成过程中的很多选项，使用下面命令能够产生一个缺省的配置文件：<br>doxygen -g&nbsp; [配置文件名]<br>可以根据项目的具体需求修改配置文件中对应的项，具体的修改过程在下面介绍。修改过的配置文件可以作为以后项目的模板。</p>
<p>让doxygen自动产生文档，平常的注释风格可不行，需要遵循doxygen自己的格式。具体如何写doxygen认识的注释在第3节详细介绍。</p>
<p>OK，代码编完了，注释也按照格式写好了，最后的文档是如何的哪？非常简单，运行下面的命令，相应的文档就会产生在指定的目录中。<br>&nbsp;&nbsp;doxygen [配置文件名]</p>
<p>需要注意的是doxygen并不处理所有的注释，doxygen重点关注与程序结构有关的注释，比如：文件、类、结构、函数、变量、宏等注释，而忽略函数内变量、代码等的注释。</p>
<p>2. doxygen配置文件<br>doxygen配置文件的格式是也是通常的unix下配置文件的格式：注释'#'开始；tag = value [,value2&#8230;]；对于多值的情况可以使用 tag += value [,value2&#8230;]。</p>
<p>对doxygen的配置文件的修改分为两类：一种就是输出选项，控制如何解释源代码、如何输出；一种就是项目相关的信息，比如项目名称、源代码目录、输出文档目录等。对于第一种设置好后，通常所有项目可以共用一份配置，而后一种是每个项目必须设置的。下面选择重要的，有可能需要修改的选项进行解释说明，其他选项在配置文件都有详细解释。</p>
<p>TAG&nbsp;缺省值&nbsp;含义<br>PROJECT_NAME&nbsp;&nbsp;项目名称<br>PROJECT_NUMBER&nbsp;&nbsp;可以理解为版本信息<br>OUTPUT_DIRECTORY&nbsp;&nbsp;输出文件到的目录，相对目录（doxygen运行目录）或者绝对目录<br>INPUT&nbsp;&nbsp;代码文件或者代码所在目录，使用空格分割<br>FILE_PATTERNS&nbsp;*.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp *.h++ *.idl *.odl&nbsp;指定INPUT的目录中特定文件，如：*.cpp *.c *.h <br>RECURSIVE&nbsp;NO&nbsp;是否递归INPUT中目录的子目录<br>EXCLUDE&nbsp;&nbsp;在INPUT目录中需要忽略的子目录<br>EXCLUDE_PATTERNS&nbsp;&nbsp;明确指定的在INPUT目录中需要忽略的文件，如：FromOut*.cpp<br>&nbsp;&nbsp;<br>OUTPUT_LANGUAGE&nbsp;English&nbsp;生成文档的语言，当前支持2、30种语言，国内用户可以设置为Chinese<br>USE_WINDOWS_ENCODING&nbsp;YES（win版本）<br>NO（unix版本）&nbsp;编码格式，默认即可。<br>EXTRACT_ALL&nbsp;NO&nbsp;为NO，只解释有doxygen格式注释的代码；为YES，解析所有代码，即使没有注释。类的私有成员和所有的静态项由EXTRACT_PRIVATE和 EXTRACT_STATIC控制<br>EXTRACT_PRIVATE&nbsp;NO&nbsp;是否解析类的私有成员<br>EXTRACT_STATIC&nbsp;NO&nbsp;是否解析静态项<br>EXTRACT_LOCAL_CLASSES&nbsp;YES&nbsp;是否解析源文件（cpp文件）中定义的类<br>SOURCE_BROWSER&nbsp;NO&nbsp;如果为YES，源代码文件会被包含在文档中<br>INLINE_SOURCES&nbsp;NO&nbsp;如果为YES，函数和类的实现代码被包含在文档中<br>ALPHABETICAL_INDEX&nbsp;NO&nbsp;生成一个字母序的列表，有很多类、结构等项时建议设为YES<br>GENERATE_HTML&nbsp;YES&nbsp;是否生成HTML格式文档<br>GENERATE_HTMLHELP&nbsp;NO&nbsp;是否生成压缩HTML格式文档（.chm）<br>GENERATE_LATEX&nbsp;YES&nbsp;是否乘车latex格式的文档<br>GENERATE_RTF&nbsp;NO&nbsp;是否生成RTF格式的文档<br>GENERATE_MAN&nbsp;NO&nbsp;是否生成man格式文档<br>GENERATE_XML&nbsp;NO&nbsp;是否生成XML格式文档<br>&nbsp;&nbsp;</p>
<p>3. doxygen注释<br>3.1 注释风格<br>下面是工作量最大部分，安装doxygen格式写注释。通常代码可以附上一个注释块来对代码进行解释，一个注释块由一行或者多行组成。通常一个注释块包括一个简要说明（brief）和一个详细说明（detailed），这两部分都是可选的。可以有多种方式标识出doxygen可识别的注释块。<br>1）JavaDoc类型的多行注释。<br>/**<br>&nbsp;*&nbsp; &#8230;.text&#8230;.<br>&nbsp;*/<br>2）QT样式的多行注释。<br>/*!<br>&#8230;.text&#8230;.<br>&nbsp;*/<br>3） /// &#8230;text&#8230;.<br>4） //! &#8230;text&#8230;.<br>简要说明有多种方式标识，这里推荐使用@brief命令强制说明，例如：<br>/**<br>&nbsp;* @brief [some brief description ]<br>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ brief description more. ]<br>&nbsp;* <br>&nbsp;* [some more detailed description&#8230;]<br>&nbsp;*/<br>以上这些注释格式用来对紧跟其后的代码进行注释。doxygen也允许把注释放到代码后面，具体格式是放一个'&lt;'到注释开始部分。例如：<br>int var1 ; /**&lt; &#8230;.text&#8230;. */<br>int var2; ///&lt; &#8230;.text&#8230;.</p>
<p>注释和代码完全分离，放在其他地方也是允许的，但需要使用特殊的命令加上名称或者声明进行标识，比如：class、struct、union、enum、fn、var、def、file、namespace、package、interface（这些也就是doxygen关注的注释类型）。这里不推荐使用，建议注释尽量放在代码前后。具体使用方式参见doxygen手册。</p>
<p>3.2 doxygen常用注释格式<br>通常的选择上面的一、两种注释风格，遇到头文件中各种类型定义，关键变量、宏的定义，在其前或者后使用 @brief 定义其简要说明，空一行后继续写其详细的注释即可。</p>
<p>对函数的注释，是比较常常需要注释的部分。除了定义其简要说明以及详细注释，还可以使用param命令对其各个参数进行注释，使用return命令对返回值进行注释。常见的格式如下：<br>/**<br>&nbsp;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#42;&#64;&#98;&#114;&#105;&#101;&#102;"><u><font color=#0000ff>*@brief</font></u></a> func's brief comment.<br>&nbsp;*<br>&nbsp;* Some detailed comment.<br>&nbsp;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#42;&#64;&#112;&#97;&#114;&#97;&#109;"><u><font color=#0000ff>*@param</font></u></a> a [param a 's comment.]<br>&nbsp;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#42;&#64;&#112;&#97;&#114;&#97;&#109;"><u><font color=#0000ff>*@param</font></u></a> b [param b 's comment.]<br>&nbsp;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#42;&#64;&#101;&#120;&#99;&#101;&#112;&#116;&#105;&#111;&#110;"><u><font color=#0000ff>*@exception</font></u></a> std::out_of_range [exception's comment.]<br>&nbsp;<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#42;&#64;&#114;&#101;&#116;&#117;&#114;&#110;"><u><font color=#0000ff>*@return</font></u></a> [return's comment.]<br>&nbsp;*/<br>int func1(int a, int b);</p>
<p>进行设计时，通常有模块的概念，一个模块可能有多个类或者函数组成，完成某个特定功能的代码的集合。如何对这个概念进行注释？doxygen提供了group的概念，生成的模块的注释会单独放在一个模块的页面中。使用下面的格式定义一个group。<br>/** [group_name] [brief group description ]<br>&nbsp;* detailed group description ]<br>&nbsp;* @{<br>*/<br>code<br>/** @} */<br>group中的代码可以有自己的注释。单纯定义一个模块，去除{ 和}命令即可。任何其他代码项（比如类、函数、甚至文件）如果要加入到某个模块，可以在其doxygen注释中使用ingroup命令即可。Group之间使用ingroup命令，可以组成树状关系。<br>/** @file util.cpp <br>* @ingroup [group_name]<br>&nbsp;* @brief file's brief info.<br>&nbsp;*/<br>把多个代码项一起添加到某个模块中可以使用addtogroup命令，格式和defgroup相似。</p>
<p>对于某几个功能类似的代码项（比如类、函数、变量）等，如果希望一起添加注释，而又不想提升到模块的概念，可以通过下面的方式：<br>//@{<br>/** Comments for all below code. */<br>code&#8230;<br>//@}<br>对这种组进行命名可以使用name命令。此时中间代码可以有自己的注释。如：<br>/** @name group_name<br>&nbsp;* description for group.<br>&nbsp;*/<br>//@{<br>code&#8230;<br>//@}</p>
<p>3.3 doxygen常用注释命令<br>doxygen通过注释命令识别注释中需要特殊处理的注释，比如函数的参数、返回值进行突出显示。上面也提到了一些注释命令（如：brief、param、return、以及group相关的命令），下面对其他一些常用的注释命令进行解释说明。<br>@exception &lt;exception-object&gt; {exception description}&nbsp;对一个异常对象进行注释。<br>@warning {warning message }&nbsp;一些需要注意的事情<br>@todo { things to be done } &nbsp;对将要做的事情进行注释<br>@see {comment with reference to other items } 一段包含其他部分引用的注释，中间包含对其他代码项的名称，自动产生对其的引用链接。<br>@relates &lt;name&gt; 通常用做把非成员函数的注释文档包含在类的说明文档中。<br>@since {text} 通常用来说明从什么版本、时间写此部分代码。<br>@deprecated<br>@pre { description of the precondition } 用来说明代码项的前提条件。<br>@post { description of the postcondition } 用来说明代码项之后的使用条件。<br>@code 在注释中开始说明一段代码，直到@endcode命令。<br>@endcode 注释中代码段的结束。</p>
<p>到此为止，常用的doxygen的注释格式讨论完毕，我们能够按照一定的格式撰写doxygen认识的注释，并能够使用doxygen方便快捷的生成对应的文档，不过注释中应该写些什么，如何撰写有效的注释可能是困扰开发人员的一个更深层次的问题。</p>
<p>4. 注释的书写<br>注释应该怎么写，写多还是写少。过多的注释甚至会干扰对代码的阅读。写注释的一个总的原则就是注释应该尽量用来表明作者的意图，至少也应该是对一部分代码的总结，而不应该是对代码的重复或者解释。对代码的重复或者解释的代码，看代码可能更容易理解。反映作者意图的注释解释代码的目的，从解决问题的层次上进行注释，而代码总结性注释则是从问题的解答的层次上进行注释。</p>
<p>推荐的写注释的过程是首先使用注释勾勒出代码的主要框架，然后根据注释撰写相应的代码。对各种主要的数据结构、输出的函数、多个函数公用的变量进行详细地注释。对代码中控制结构，单一目的的语句集进行注释。下面是一些写注释时需要注意的要点：<br>&nbsp;&nbsp;避免对单独语句进行注释；<br>&nbsp;&nbsp;通过注释解释为什么这么做、或者要做什么，使代码的读者可以只阅读注释理解代码；<br>&nbsp;&nbsp;对读者可能会有疑问的地方进行注释；<br>&nbsp;&nbsp;对数据定义进行注释，而不是对其使用过程进行注释；<br>&nbsp;&nbsp;对于难于理解的代码，进行改写，而不要试图通过注释加以说明；<br>&nbsp;&nbsp;对关键的控制结构进行注释；<br>&nbsp;&nbsp;对数据和函数的边界、使用前提等进行注释；</p>
<p>5. 参考资料<br>&nbsp;1. doxygen homepage<br>&nbsp;<a href="http://www.stack.nl/~dimitri/doxygen/"><u><font color=#0000ff>http://www.stack.nl/~dimitri/doxygen/</font></u></a></p>
<p>&nbsp;2. doxygen manual <br>&nbsp;<a href="http://www.stack.nl/~dimitri/doxygen/manual.html"><u><font color=#0000ff>http://www.stack.nl/~dimitri/doxygen/manual.html</font></u></a></p>
<p>&nbsp;3. Code Complete: A Practical Handbook of Software Construction. Redmond, Wa.: Microsoft Press, 880 pages, 1993. ISBN: 1-55615-484-4. <br>&nbsp;<br>&nbsp;4. 简介doxygen<br>&nbsp;<a href="http://www.stack.nl/~dimitri/doxygen/doxygen_intro_cn.html"><u><font color=#800080>http://www.stack.nl/~dimitri/doxygen/doxygen_intro_cn.html</font></u></a><br>&nbsp;<br>&nbsp;5. 10 Minutes to document your code<br>&nbsp;<a href="http://www.codeproject.com/tips/doxysetup.asp"><u><font color=#0000ff>http://www.codeproject.com/tips/doxysetup.asp</font></u></a></p>
<p>&nbsp;6. 使用doxygen<br>&nbsp;<a href="http://www.csdn.net/Develop/article/16%5C16383.shtm"><u><font color=#0000ff>http://www.csdn.net/Develop/article/16%5C16383.shtm</font></u></a></p>
<p>&nbsp;<br>6. 关于作者<br>mounton @ {www.ihere.org} 当前关注于网络安全产品的开发、研究；软件开发过程等方面。您可以通过<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#109;&#111;&#117;&#110;&#116;&#48;&#110;&#64;&#121;&#97;&#104;&#111;&#111;&#46;&#99;&#111;&#109;"><u><font color=#0000ff>mount0n@yahoo.com</font></u></a>和他联系。<br></p>
<img height=1 src="http://www.moon-soft.com/doc/down_info.asp?id=39077" width=1 border=0><br>
<img src ="http://www.blogjava.net/jinfeng_wang/aggbug/121339.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/jinfeng_wang/" target="_blank">jinfeng_wang</a> 2007-06-01 13:17 <a href="http://www.blogjava.net/jinfeng_wang/archive/2007/06/01/121339.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>