﻿<?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>IT博客-大话人生-随笔分类-测试基础</title><link>http://www.cnitblog.com/stomic/category/7070.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 22 Sep 2015 05:18:01 GMT</lastBuildDate><pubDate>Tue, 22 Sep 2015 05:18:01 GMT</pubDate><ttl>60</ttl><item><title>（转）缓存、缓存算法和缓存框架简介</title><link>http://www.cnitblog.com/stomic/archive/2015/09/16/90212.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Wed, 16 Sep 2015 09:52:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2015/09/16/90212.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/90212.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2015/09/16/90212.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/90212.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/90212.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 引言我们都听过 cache，当你问他们是什么是缓存的时候，他们会给你一个完美的答案，可是他们不知道缓存是怎么构建的，或者没有告诉你应该采用什么标准去选择缓存框架。在这边文章，我们会去讨论缓存，缓存算法，缓存框架以及哪个缓存框架会更好。面试&#8220;缓存就是存贮数据（使用频繁的数据）的临时地方，因为取原始数据的代价太大了，所以我可以取得快一些。&#8221;这就是 programmer one ...&nbsp;&nbsp;<a href='http://www.cnitblog.com/stomic/archive/2015/09/16/90212.html'>阅读全文</a><img src ="http://www.cnitblog.com/stomic/aggbug/90212.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2015-09-16 17:52 <a href="http://www.cnitblog.com/stomic/archive/2015/09/16/90212.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA 对象引用，以及对象赋值</title><link>http://www.cnitblog.com/stomic/archive/2015/06/17/90125.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Wed, 17 Jun 2015 08:25:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2015/06/17/90125.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/90125.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2015/06/17/90125.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/90125.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/90125.html</trackback:ping><description><![CDATA[<p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">Java</span>对象及其引用</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">关于对象与引用之间的一些基本概念。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;初学<span style="font-family: 'Times New Roman';">Java</span>时，在很长一段时间里，总觉得基本概念很模糊。后来才知道，在许多<span style="font-family: 'Times New Roman';">Java</span>书中，把对象和对象的引用混为一谈。可是，如果我分不清对象与对象引用，</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;那实在没法很好地理解下面的面向对象技术。把自己的一点认识写下来，或许能让初学<span style="font-family: 'Times New Roman';">Java</span>的朋友们少走一点弯路。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;为便于说明，我们先定义一个简单的类：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class Vehicle {</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int passengers;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int fuelcap;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int mpg;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">有了这个模板，就可以用它来创建对象：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Vehicle veh1 = new Vehicle();</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">通常把这条语句的动作称之为创建一个对象，其实，它包含了四个动作。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">1</span>）右边的&#8220;<span style="font-family: 'Times New Roman';">new Vehicle</span>&#8221;，是以<span style="font-family: 'Times New Roman';">Vehicle</span>类为模板，在堆空间里创建一个<span style="font-family: 'Times New Roman';">Vehicle</span>类对象（也简称为<span style="font-family: 'Times New Roman';">Vehicle</span>对象）。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">2</span>）末尾的<span style="font-family: 'Times New Roman';">()</span>意味着，在对象创建后，立即调用<span style="font-family: 'Times New Roman';">Vehicle</span>类的构造函数，对刚生成的对象进行初始化。构造函数是肯定有的。如果你没写，<span style="font-family: 'Times New Roman';">Java</span>会给你补上一个默认的构造函数。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">3</span>）左边的&#8220;<span style="font-family: 'Times New Roman';">Vehicle veh</span>&nbsp;<span style="font-family: 'Times New Roman';">1</span>&#8221;创建了一个<span style="font-family: 'Times New Roman';">Vehicle</span>类引用变量。所谓<span style="font-family: 'Times New Roman';">Vehicle</span>类引用，就是以后可以用来指向<span style="font-family: 'Times New Roman';">Vehicle</span>对象的对象引用。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">4</span>）&#8220;<span style="font-family: 'Times New Roman';">=</span>&#8221;操作符使对象引用指向刚创建的那个<span style="font-family: 'Times New Roman';">Vehicle</span>对象。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">我们可以把这条语句拆成两部分：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">Vehicle veh1;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">veh1 = new Vehicle();</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">效果是一样的。这样写，就比较清楚了，有两个实体：一是对象引用变量，一是对象本身。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;在堆空间里创建的实体，与在数据段以及栈空间里创建的实体不同。尽管它们也是确确实实存在的实体，但是，我们看不见，也摸不着。不仅如此，</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;我们仔细研究一下第二句，找找刚创建的对象叫什么名字？有人说，它叫&#8220;<span style="font-family: 'Times New Roman';">Vehicle</span>&#8221;。不对，&#8220;<span style="font-family: 'Times New Roman';">Vehicle</span>&#8221;是类（对象的创建模板）的名字。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;一个<span style="font-family: 'Times New Roman';">Vehicle</span>类可以据此创建出无数个对象，这些对象不可能全叫&#8220;<span style="font-family: 'Times New Roman';">Vehicle</span>&#8221;。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;对象连名都没有，没法直接访问它。我们只能通过对象引用来间接访问对象。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;为了形象地说明对象、引用及它们之间的关系，可以做一个或许不很妥当的比喻。对象好比是一只很大的气球，大到我们抓不住它。引用变量是一根绳， 可以用来系汽球。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;如果只执行了第一条语句，还没执行第二条，此时创建的引用变量<span style="font-family: 'Times New Roman';">veh1</span>还没指向任何一个对象，它的值是<span style="font-family: 'Times New Roman';">null</span>。引用变量可以指向某个对象，或者为<span style="font-family: 'Times New Roman';">null</span>。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;它是一根绳，一根还没有系上任何一个汽球的绳。执行了第二句后，一只新汽球做出来了，并被系在<span style="font-family: 'Times New Roman';">veh1</span>这根绳上。我们抓住这根绳，就等于抓住了那只汽球。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;再来一句：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Vehicle veh2;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">就又做了一根绳，还没系上汽球。如果再加一句：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; veh2 = veh1;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">系上了。这里，发生了复制行为。但是，要说明的是，对象本身并没有被复制，被复制的只是对象引用。结果是，<span style="font-family: 'Times New Roman';">veh2</span>也指向了<span style="font-family: 'Times New Roman';">veh1</span>所指向的对象。两根绳系的是同一只汽球。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;如果用下句再创建一个对象：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">veh2 = new Vehicle();</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">则引用变量<span style="font-family: 'Times New Roman';">veh2</span>改指向第二个对象。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;从以上叙述再推演下去，我们可以获得以下结论：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">（<span style="font-family: 'Times New Roman';">1</span>）一个对象引用可以指向<span style="font-family: 'Times New Roman';">0</span>个或<span style="font-family: 'Times New Roman';">1</span>个对象（一根绳子可以不系汽球，也可以系一个汽球）；</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">（<span style="font-family: 'Times New Roman';">2</span>）一个对象可以有<span style="font-family: 'Times New Roman';">N</span>个引用指向它（可以有<span style="font-family: 'Times New Roman';">N</span>条绳子系住一个汽球）。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;如果再来下面语句：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; veh1 = veh2;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">按上面的推断，<span style="font-family: 'Times New Roman';">veh1</span>也指向了第二个对象。这个没问题。问题是第一个对象呢？没有一条绳子系住它，它飞了。多数书里说，它被<span style="font-family: 'Times New Roman';">Java</span>的垃圾回收机制回收了。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">这不确切。正确地说，它已成为垃圾回收机制的处理对象。至于什么时候真正被回收，那要看垃圾回收机制的心情了。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;由此看来，下面的语句应该不合法吧？至少是没用的吧？</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">new Vehicle();</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">不对。它是合法的，而且可用的。譬如，如果我们仅仅为了打印而生成一个对象，就不需要用引用变量来系住它。最常见的就是打印字符串：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(&#8220;I am Java!&#8221;);</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">字符串对象&#8220;<span style="font-family: 'Times New Roman';">I am Java!</span>&#8221;在打印后即被丢弃。有人把这种对象称之为临时对象。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;对象与引用的关系将持续到对象回收。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">&nbsp;&nbsp;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">Java</span>对象及引用</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">Java</span>对象及引用是容易混淆却又必须掌握的基础知识，本章阐述<span style="font-family: 'Times New Roman';">Java</span>对象和引用的概念，以及与其密切相关的参数传递。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">先看下面的程序：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">StringBuffer s;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">s = new StringBuffer("Hello World!");</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">第一个语句仅为引用<span style="font-family: 'Times New Roman';">(reference)</span>分配了空间，而第二个语句则通过调用类<span style="font-family: 'Times New Roman';">(StringBuffer)</span>的构造函数<span style="font-family: 'Times New Roman';">StringBuffer(String str)</span>为类生成了一个实例（或称为对象）。这两个操作被完成后，对象的内容则可通过<span style="font-family: 'Times New Roman';">s</span>进行访问&#8212;&#8212;在<span style="font-family: 'Times New Roman';">Java</span>里都是通过引用来操纵对象的。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">Java</span>对象和引用的关系可以说是互相关联，却又彼此独立。彼此独立主要表现在：引用是可以改变的，它可以指向别的对象，譬如上面的<span style="font-family: 'Times New Roman';">s</span>，你可以给它另外的对象，如：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">s = new StringBuffer("Java");</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">这样一来，<span style="font-family: 'Times New Roman';">s</span>就和它指向的第一个对象脱离关系。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">从存储空间上来说，对象和引用也是独立的，它们存储在不同的地方，对象一般存储在堆中，而引用存储在速度更快的堆栈中。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">引用可以指向不同的对象，对象也可以被多个引用操纵，如：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">StringBuffer s1 = s;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">这条语句使得<span style="font-family: 'Times New Roman';">s1</span>和<span style="font-family: 'Times New Roman';">s</span>指向同一个对象。既然两个引用指向同一个对象，那么不管使用哪个引用操纵对象，对象的内容都发生改变，并且只有一份，通过<span style="font-family: 'Times New Roman';">s1</span>和<span style="font-family: 'Times New Roman';">s</span>得到的内容自然也一样，<span style="font-family: 'Times New Roman';">(String</span>除外，因为<span style="font-family: 'Times New Roman';">String</span>始终不变，<span style="font-family: 'Times New Roman';">String s1=&#8221;AAAA&#8221;; String s=s1,</span>操作<span style="font-family: 'Times New Roman';">s,s1</span>由于始终不变，所以为<span style="font-family: 'Times New Roman';">s</span>另外开辟了空间来存储<span style="font-family: 'Times New Roman';">s,)</span>如下面的程序：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">StringBuffer s;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">s = new StringBuffer("Java");</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">StringBuffer s1 = s;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">s1.append(" World");</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">System.out.println("s1=" + s1.toString());//</span>打印结果为：<span style="font-family: 'Times New Roman';">s1=Java World</span></span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">System.out.println("s=" + s.toString());//</span>打印结果为：<span style="font-family: 'Times New Roman';">s=Java World</span></span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">&nbsp;&nbsp;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">上面的程序表明，<span style="font-family: 'Times New Roman';">s1</span>和<span style="font-family: 'Times New Roman';">s</span>打印出来的内容是一样的，这样的结果看起来让人非常疑惑，但是仔细想想，<span style="font-family: 'Times New Roman';">s1</span>和<span style="font-family: 'Times New Roman';">s</span>只是两个引用，它们只是操纵杆而已，它们指向同一个对象，操纵的也是同一个对象，通过它们得到的是同一个对象的内容。这就像汽车的刹车和油门，它们操纵的都是车速，假如汽车开始的速度是<span style="font-family: 'Times New Roman';">80</span>，然后你踩了一次油门，汽车加速了，假如车速升到了<span style="font-family: 'Times New Roman';">120</span>，然后你踩一下刹车，此时车速是从<span style="font-family: 'Times New Roman';">120</span>开始下降的，假如下降到<span style="font-family: 'Times New Roman';">60</span>，再踩一次油门，车速则从<span style="font-family: 'Times New Roman';">60</span>开始上升，而不是从第一次踩油门后的<span style="font-family: 'Times New Roman';">120</span>开始。也就是说车速同时受油门和刹车影响，它们的影响是累积起来的，而不是各自独立（除非刹车和油门不在一辆车上）。所以，在上面的程序中，不管使用<span style="font-family: 'Times New Roman';">s1</span>还是<span style="font-family: 'Times New Roman';">s</span>操纵对象，它们对对象的影响也是累积起来的（更多的引用同理）。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">只有理解了对象和引用的关系，才能理解参数传递。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">一般面试题中都会考<span style="font-family: 'Times New Roman';">Java</span>传参的问题，并且它的标准答案是<span style="font-family: 'Times New Roman';">Java</span>只有一种参数传递方式：那就是按值传递，即<span style="font-family: 'Times New Roman';">Java</span>中传递任何东西都是传值。如果传入方法的是基本类型的东西，你就得到此基本类型的一份拷贝。如果是传递引用，就得到引用的拷贝。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">一般来说，对于基本类型的传递，我们很容易理解，而对于对象，总让人感觉是按引用传递，看下面的程序：</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">public class ObjectRef {</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp; //</span>基本类型的参数传递</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp; public static void testBasicType(int m) {</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("m=" + m);//m=50</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m = 100;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("m=" + m);//m=100</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp; }</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp; //</span>参数为对象，不改变引用的值 ？？？？？？</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp; public static void add(StringBuffer s) {</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s.append("_add");</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp; }</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp; //</span>参数为对象，改变引用的值 ？？？？？</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp; public static void changeRef(StringBuffer s) {</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s = new StringBuffer("Java");</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp; }</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp; public static void main(String[] args) {</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int i = 50;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testBasicType(i);</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(i);//i=50</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuffer sMain = new StringBuffer("init");</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("sMain=" + sMain.toString());//sMain=init</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add(sMain);</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("sMain=" + sMain.toString());//sMain=init_add</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; changeRef(sMain);</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("sMain=" + sMain.toString());//sMain=init_add</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp; }</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">}</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">以上程序的允许结果显示出，<span style="font-family: 'Times New Roman';">testBasicType</span>方法的参数是基本类型，尽管参数<span style="font-family: 'Times New Roman';">m</span>的值发生改变，但并不影响<span style="font-family: 'Times New Roman';">i</span>。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; add</span>方法的参数是一个对象，当把<span style="font-family: 'Times New Roman';">sMain</span>传给参数<span style="font-family: 'Times New Roman';">s</span>时，<span style="font-family: 'Times New Roman';">s</span>得到的是<span style="font-family: 'Times New Roman';">sMain</span>的拷贝，所以<span style="font-family: 'Times New Roman';">s</span>和<span style="font-family: 'Times New Roman';">sMain</span>指向同一个对象，因此，使用<span style="font-family: 'Times New Roman';">s</span>操作影响的其实就是<span style="font-family: 'Times New Roman';">sMain</span>指向的对象，故调用<span style="font-family: 'Times New Roman';">add</span>方法后，<span style="font-family: 'Times New Roman';">sMain</span>指向的对象的内容发生了改变。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>&nbsp;在<span style="font-family: 'Times New Roman';">changeRef</span>方法中，参数也是对象，当把<span style="font-family: 'Times New Roman';">sMain</span>传给参数<span style="font-family: 'Times New Roman';">s</span>时，<span style="font-family: 'Times New Roman';">s</span>得到的是<span style="font-family: 'Times New Roman';">sMain</span>的拷贝，但与<span style="font-family: 'Times New Roman';">add</span>方法不同的是，<strong>在方法体内改变了</strong><strong><span style="font-family: 'Times New Roman';">s</span></strong><strong>指向的对象（也就是</strong><strong><span style="font-family: 'Times New Roman';">s</span></strong><strong>指向了别的对象</strong><strong><span style="font-family: 'Times New Roman';">,</span></strong><strong>牵着气球的绳子换气球了</strong><strong>），给</strong><strong><span style="font-family: 'Times New Roman';">s</span></strong><strong>重新赋值后，</strong><strong><span style="font-family: 'Times New Roman';">s</span></strong><strong>与</strong><strong><span style="font-family: 'Times New Roman';">sMain</span></strong><strong>已经毫无关联</strong>，它和<span style="font-family: 'Times New Roman';">sMain</span>指向了不同的对象，所以不管对<span style="font-family: 'Times New Roman';">s</span>做什么操作，都不会影响<span style="font-family: 'Times New Roman';">sMain</span>指向的对象，故调用<span style="font-family: 'Times New Roman';">changeRef</span>方法前后<span style="font-family: 'Times New Roman';">sMain</span>指向的对象内容并未发生改变。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">对于<span style="font-family: 'Times New Roman';">add</span>方法的调用结果，可能很多人会有这种感觉：这不明明是按引用传递吗？对于这种问题，还是套用<span style="font-family: 'Times New Roman';">Bruce Eckel</span>的话：这依赖于你如何看待引用，最终你会明白，这个争论并没那么重要。真正重要的是，你要理解，传引用使得（调用者的）对象的修改变得不可预期。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;"><span style="font-family: 'Times New Roman';">&nbsp;public&nbsp;&nbsp; class&nbsp;&nbsp; Test<br />{&nbsp;&nbsp; public int&nbsp;&nbsp; i,j;&nbsp;&nbsp;<br />&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; void&nbsp;&nbsp; test_m(Test&nbsp;&nbsp; a)<br />&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp; Test&nbsp;&nbsp; b&nbsp;&nbsp; =&nbsp; new&nbsp;&nbsp; Test();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b.i&nbsp;&nbsp; =&nbsp;&nbsp; 1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; b.j&nbsp;&nbsp; =&nbsp;&nbsp; 2;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a&nbsp;&nbsp; =&nbsp;&nbsp; b;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; void&nbsp;&nbsp; test_m1(Test&nbsp;&nbsp; a&nbsp;&nbsp; )<br />&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp; a.i&nbsp;&nbsp; =&nbsp;&nbsp; 1;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; a.j&nbsp;&nbsp; =&nbsp;&nbsp; 2;<br />&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp; public&nbsp;&nbsp; static&nbsp;&nbsp; void&nbsp;&nbsp; main(String&nbsp;&nbsp; argv[])<br />&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp; Test&nbsp;&nbsp; t=&nbsp;&nbsp; new&nbsp;&nbsp; Test();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.i&nbsp;&nbsp; =&nbsp;&nbsp; 5;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.j&nbsp;&nbsp; =&nbsp;&nbsp; 6;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( "t.i&nbsp;&nbsp; =&nbsp;&nbsp; "+&nbsp;&nbsp; t.i&nbsp;&nbsp; +&nbsp;&nbsp; "&nbsp;&nbsp; t.j=&nbsp;&nbsp; "&nbsp;&nbsp; +&nbsp;&nbsp; t.j); //5,6<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.test_m(t);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( "t.i&nbsp;&nbsp; =&nbsp;&nbsp; "+&nbsp;&nbsp; t.i&nbsp;&nbsp; +&nbsp;&nbsp; "&nbsp;&nbsp; t.j=&nbsp;&nbsp; "&nbsp;&nbsp; +&nbsp;&nbsp; t.j); //5,6,</span><span style="font-family: 'Times New Roman';">a</span>和<span style="font-family: 'Times New Roman';">t</span>都指向了一个对象，而在<span style="font-family: 'Times New Roman';">test_m</span>中<span style="font-family: 'Times New Roman';">s</span>又指向了另一个对象，所以对象<span style="font-family: 'Times New Roman';">t</span>不变！！！</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; t.test_m1(t);</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println( "t.i&nbsp;&nbsp; =&nbsp;&nbsp; "+&nbsp;&nbsp; t.i&nbsp;&nbsp; +&nbsp;&nbsp; "&nbsp;&nbsp; t.j=&nbsp;&nbsp; "&nbsp;&nbsp; +&nbsp;&nbsp; t.j); //1,2</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">&nbsp;&nbsp;&nbsp; }</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-family: 'Times New Roman'; font-size: 12px;">}</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">答案只有一个：<span style="font-family: 'Times New Roman';">Java</span>里都是按值传递参数。而实际上，我们要明白，当参数是对象时，传引用会发生什么状况（就像上面的<span style="font-family: 'Times New Roman';">add</span>方法）？</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">=========================================================================</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">楼主，这样来记这个问题<br />如下表达式：<br />A a1 = new A();<br />它代表A是类，a1是引用，a1不是对象，new A()才是对象，a1引用指向new A()这个对象。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">在JAVA里，&#8220;=&#8221;不能被看成是一个赋值语句，它不是在把一个对象赋给另外一个对象，它的执行过程实质上是将右边对象的地址传给了左边的引用，使得左边的引用指向了右边的对象。JAVA表面上看起来没有指针，但它的引用其实质就是一个指针，引用里面存放的并不是对象，而是该对象的地址，使得该引用指向了对象。在JAVA里，&#8220;=&#8221;语句不应该被翻译成赋值语句，因为它所执行的确实不是一个赋值的过程，而是一个传地址的过程，被译成赋值语句会造成很多误解，译得不准确。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">再如：<br />A a2;<br />它代表A是类，a2是引用，a2不是对象，a2所指向的对象为空null;</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">再如：<br />a2 = a1;<br />它代表，a2是引用，a1也是引用，a1所指向的对象的地址传给了a2(传址），使得a2和a1指向了同一对象。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">综上所述，可以简单的记为，在初始化时，&#8220;=&#8221;语句左边的是引用，右边new出来的是对象。<br />在后面的左右都是引用的&#8220;=&#8221;语句时，左右的引用同时指向了右边引用所指向的对象。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><span style="font-size: 12px;">再所谓实例，其实就是对象的同义词。</span></p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">&nbsp;</p><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">如果需要赋值，就需要类实现Cloneable接口，实现clone()方法。</p><div style="font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><table style="border-style: solid; border-color: silver; border-collapse: collapse; word-break: break-word;"><tbody><tr><td style="border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">1 2 3 4 5 6 7 8 9 10 11 </pre></td><td style="border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"><span style="font-weight: bold;">class</span> D <span style="font-weight: bold;">implements</span> <span style="color: #003399;">Cloneable</span><span style="color: #009900;">{</span><span style="font-style: italic; color: #666666;">//实现Cloneable接口</span> 	<span style="color: #003399;">String</span> sex<span style="color: #339933;">;</span> 	D<span style="color: #009900;">(</span><span style="color: #003399;">String</span> sex<span style="color: #009900;">)</span><span style="color: #009900;">{</span> 		<span style="font-weight: bold;">this</span>.<span style="color: #006633;">sex</span><span style="color: #339933;">=</span>sex<span style="color: #339933;">;</span> 	<span style="color: #009900;">}</span> 	@Override 	<span style="font-weight: bold;">protected</span> <span style="color: #003399;">Object</span> clone<span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="font-weight: bold;">throws</span> <span style="color: #003399;">CloneNotSupportedException</span> <span style="color: #009900;">{</span> 		<span style="font-style: italic; color: #666666;">// 实现clone方法</span> 		<span style="font-weight: bold;">return</span> <span style="font-weight: bold;">super</span>.<span style="color: #006633;">clone</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span> 	<span style="color: #009900;">}</span> <span style="color: #009900;">}</span></pre></td></tr></tbody></table></div><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">赋值的时候：</p><div style="font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><table style="border-style: solid; border-color: silver; border-collapse: collapse; word-break: break-word;"><tbody><tr><td style="border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">1 2 </pre></td><td style="border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">D d<span style="color: #339933;">=</span><span style="font-weight: bold;">new</span> D<span style="color: #009900;">(</span><span style="color: #0000ff;">"男"</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span> D d2<span style="color: #339933;">=</span><span style="color: #009900;">(</span>D<span style="color: #009900;">)</span> d.<span style="color: #006633;">clone</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span><span style="font-style: italic; color: #666666;">//把d赋值给d2</span></pre></td></tr></tbody></table></div><p style="margin: 10px auto; font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;">如果类中的变量不是主类型，而是对象，也需要调用该对象的clone()方法<br />下面是一个完整的例子：</p><div style="font-family: verdana, sans-serif; font-size: 13px; line-height: 19.5px;"><table style="border-style: solid; border-color: silver; border-collapse: collapse; word-break: break-word;"><tbody><tr><td style="border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 </pre></td><td style="border-style: solid; border-color: silver; border-collapse: collapse; padding: 3px;"><pre style="margin-top: 0px; margin-bottom: 0px; white-space: pre-wrap; word-wrap: break-word;"><span style="font-weight: bold;">public</span> <span style="font-weight: bold;">class</span> Test2 <span style="color: #009900;">{</span> 	<span style="font-weight: bold;">public</span> <span style="font-weight: bold;">static</span> <span style="color: #000066; font-weight: bold;">void</span> main<span style="color: #009900;">(</span><span style="color: #003399;">String</span><span style="color: #009900;">[</span><span style="color: #009900;">]</span> args<span style="color: #009900;">)</span> <span style="font-weight: bold;">throws</span> <span style="color: #003399;">CloneNotSupportedException</span> <span style="color: #009900;">{</span> 		<span style="font-style: italic; color: #666666;">// TODO Auto-generated method stub</span> 		D d<span style="color: #339933;">=</span><span style="font-weight: bold;">new</span> D<span style="color: #009900;">(</span><span style="color: #0000ff;">"男"</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span> 		C c<span style="color: #339933;">=</span><span style="font-weight: bold;">new</span> C<span style="color: #009900;">(</span><span style="color: #0000ff;">"张三"</span>,<span style="color: #0000ff;">"20"</span>,d<span style="color: #009900;">)</span><span style="color: #339933;">;</span> 		C new_c<span style="color: #339933;">=</span><span style="color: #009900;">(</span>C<span style="color: #009900;">)</span> c.<span style="color: #006633;">clone</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span><span style="font-style: italic; color: #666666;">//调用clone方法来赋值</span> 		new_c.<span style="color: #006633;">name</span><span style="color: #339933;">=</span><span style="color: #0000ff;">"李四"</span><span style="color: #339933;">;</span> 		d.<span style="color: #006633;">sex</span><span style="color: #339933;">=</span><span style="color: #0000ff;">"女"</span><span style="color: #339933;">;</span><span style="font-style: italic; color: #666666;">//d</span> 		<span style="color: #003399;">System</span>.<span style="color: #006633;">out</span>.<span style="color: #006633;">println</span><span style="color: #009900;">(</span>c.<span style="color: #006633;">d</span>.<span style="color: #006633;">sex</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span> 		<span style="color: #003399;">System</span>.<span style="color: #006633;">out</span>.<span style="color: #006633;">println</span><span style="color: #009900;">(</span>c.<span style="color: #006633;">name</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span> &nbsp; 	<span style="color: #009900;">}</span> &nbsp; <span style="color: #009900;">}</span> &nbsp; <span style="font-weight: bold;">class</span> C <span style="font-weight: bold;">implements</span> <span style="color: #003399;">Cloneable</span><span style="color: #009900;">{</span> 	<span style="color: #003399;">String</span> name<span style="color: #339933;">;</span> 	<span style="color: #003399;">String</span> age<span style="color: #339933;">;</span> 	D d<span style="color: #339933;">;</span> 	C<span style="color: #009900;">(</span><span style="color: #003399;">String</span> name,<span style="color: #003399;">String</span> age,D d<span style="color: #009900;">)</span> <span style="font-weight: bold;">throws</span> <span style="color: #003399;">CloneNotSupportedException</span><span style="color: #009900;">{</span> 		<span style="font-weight: bold;">this</span>.<span style="color: #006633;">name</span><span style="color: #339933;">=</span>name<span style="color: #339933;">;</span> 		<span style="font-weight: bold;">this</span>.<span style="color: #006633;">age</span><span style="color: #339933;">=</span>age<span style="color: #339933;">;</span> 		<span style="font-weight: bold;">this</span>.<span style="color: #006633;">d</span><span style="color: #339933;">=</span><span style="color: #009900;">(</span>D<span style="color: #009900;">)</span> d.<span style="color: #006633;">clone</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span><span style="font-style: italic; color: #666666;">//调用clone方法来赋值，这样即便外部的d发生变化，c里的也不会变</span> 	<span style="color: #009900;">}</span> 	@Override 	<span style="font-weight: bold;">protected</span> <span style="color: #003399;">Object</span> clone<span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="font-weight: bold;">throws</span> <span style="color: #003399;">CloneNotSupportedException</span> <span style="color: #009900;">{</span> 		<span style="font-style: italic; color: #666666;">// TODO Auto-generated method stub</span> 		<span style="font-weight: bold;">return</span> <span style="font-weight: bold;">super</span>.<span style="color: #006633;">clone</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span> 	<span style="color: #009900;">}</span> <span style="color: #009900;">}</span> <span style="font-weight: bold;">class</span> D <span style="font-weight: bold;">implements</span> <span style="color: #003399;">Cloneable</span><span style="color: #009900;">{</span><span style="font-style: italic; color: #666666;">//实现Cloneable接口</span> 	<span style="color: #003399;">String</span> sex<span style="color: #339933;">;</span> 	D<span style="color: #009900;">(</span><span style="color: #003399;">String</span> sex<span style="color: #009900;">)</span><span style="color: #009900;">{</span> 		<span style="font-weight: bold;">this</span>.<span style="color: #006633;">sex</span><span style="color: #339933;">=</span>sex<span style="color: #339933;">;</span> 	<span style="color: #009900;">}</span> 	@Override 	<span style="font-weight: bold;">protected</span> <span style="color: #003399;">Object</span> clone<span style="color: #009900;">(</span><span style="color: #009900;">)</span> <span style="font-weight: bold;">throws</span> <span style="color: #003399;">CloneNotSupportedException</span> <span style="color: #009900;">{</span> 		<span style="font-style: italic; color: #666666;">// 实现clone方法</span> 		<span style="font-weight: bold;">return</span> <span style="font-weight: bold;">super</span>.<span style="color: #006633;">clone</span><span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span> 	<span style="color: #009900;">}</span> <span style="color: #009900;">}</span></pre></td></tr></tbody></table><p style="margin: 10px auto;">原文：<a href="http://blog.sina.com.cn/s/blog_4cd5d2bb0100ve9r.html" style="color: #444444;">http://blog.sina.com.cn/s/blog_4cd5d2bb0100ve9r.html</a></p><p style="margin: 10px auto;">clone:<a href="http://bxkqzjt521.blog.163.com/blog/static/202818420119102362458/" style="color: #444444;">http://bxkqzjt521.blog.163.com/blog/static/202818420119102362458/</a></p></div><img src ="http://www.cnitblog.com/stomic/aggbug/90125.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2015-06-17 16:25 <a href="http://www.cnitblog.com/stomic/archive/2015/06/17/90125.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Cookie/Session机制详解 </title><link>http://www.cnitblog.com/stomic/archive/2014/11/27/89867.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Thu, 27 Nov 2014 03:52:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2014/11/27/89867.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/89867.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2014/11/27/89867.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/89867.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/89867.html</trackback:ping><description><![CDATA[<div><p align="left">会话（Session）跟踪是Web程序中常用的技术，用来<strong>跟踪用户的整个会话</strong>。常用的会话跟踪技术是Cookie与Session。<span style="color:#FF0000"><strong>Cookie通过在客户端记录信息确定用户身份</strong>，<strong>Session通过在服务器端记录信息确定用户身份</strong></span>。</p> <p align="left">本章将系统地讲述Cookie与Session机制，并比较说明什么时候不能用Cookie，什么时候不能用Session。</p> <p align="left"><br /> </p> <p align="left"><strong>1.1&nbsp; Cookie机制</strong></p> <p align="left">在程序中，会话跟踪是很重要的事情。理论上，<span style="color:#FF0000"><strong>一个用户的所有请求操作都应该属于同一个会话</strong></span>，而另一个用户的所有请求操作则应该属于另一个会话，二者不能混淆。例如，用户A在超市购买的任何商品都应该放在A的购物车内，不论是用户A什么时间购买的，这都是属于同一个会话的，不能放入用户B或用户C的购物车内，这不属于同一个会话。</p> <p align="left">而Web应用程序是使用HTTP协议传输数据的。<span style="color:#FF0000"><strong>HTTP协议是无状态的协议。一旦数据交换完毕，客户端与服务器端的连接就会关闭，再次交换数据需要建立新的连接。这就意味着服务器无法从连接上跟踪会话</strong>。</span>即用户A购买了一件商品放入购物车内，当再次购买商品时服务器已经无法判断该购买行为是属于用户A的会话还是用户B的会话了。要跟踪该会话，必须引入一种机制。</p> <p align="left">Cookie就是这样的一种机制。它可以弥补HTTP协议无状态的不足。在Session出现之前，基本上所有的网站都采用Cookie来跟踪会话。</p> <p align="left"><strong>1.1.1&nbsp; 什么是Cookie</strong></p> <p align="left">Cookie意为&#8220;甜饼&#8221;，是<strong>由W3C组织提出</strong>，最早由Netscape社区发展的一种机制。目前Cookie已经成为标准，所有的主流浏览器如IE、Netscape、Firefox、Opera等都支持Cookie。</p> <p align="left">由于HTTP是一种无状态的协议，服务器单从网络连接上无从知道客户身份。怎么办呢？就<span style="color:#FF0000"><strong>给客户端们颁发一个通行证吧，每人一个，无论谁访问都必须携带自己通行证。这样服务器就能从通行证上确认客户身份了。这就是Cookie的工作原理</strong>。</span></p> <p align="left">Cookie实际上是一小段的文本信息。客户端请求服务器，如果服务器需要记录该用户状态，就使用response向客 户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。当浏览器再请求该网站时，浏览器把请求的网址连同该Cookie一同提交给服务 器。服务器检查该Cookie，以此来辨认用户状态。服务器还可以根据需要修改Cookie的内容。</p> <div><img src="http://hi.csdn.net/attachment/201111/9/0_13208318702UfF.gif" alt="" height="277" width="447" /></div> <p align="left"><br /> </p> <p align="left"><br /> </p> <p align="left">查看某个网站颁发的Cookie很简单。在浏览器地址栏输入<span style="color:#FF0000"><strong>javascript:alert (document. cookie)</strong></span>就可以了（需要有网才能查看）。JavaScript脚本会弹出一个对话框显示本网站颁发的所有Cookie的内容，如图1.1所示。</p> <p align="left"><img src="http://hi.csdn.net/attachment/201111/9/0_1320831961Yumf.gif" alt="" /><br /> </p> <p align="left">图1.1&nbsp; Baidu网站颁发的Cookie</p> <p align="left"><br /> </p> <p align="left">图1.1中弹出的对话框中显示的为Baidu网站的Cookie。其中第一行BAIDUID记录的就是笔者的身份helloweenvsfei，只是Baidu使用特殊的方法将Cookie信息加密了。</p> <p align="left"><br /> </p> <p align="left">注意：Cookie功能需要浏览器的支持。</p> <p align="left">如果浏览器不支持Cookie（如大部分手机中的浏览器）或者把Cookie禁用了，Cookie功能就会失效。</p> <p align="left">不同的浏览器采用不同的方式保存Cookie。</p> <p align="left">IE浏览器会在&#8220;C:\Documents and Settings\你的用户名\Cookies&#8221;文件夹下以文本文件形式保存，一个文本文件保存一个Cookie。</p> <p align="left"><br /> </p> <p align="left"><strong>1.1.2&nbsp; 记录用户访问次数</strong></p> <p align="left">Java中把Cookie封装成了javax.servlet.http.Cookie类。每个Cookie都是该Cookie类的对象。服务器通过操作Cookie类对象对客户端Cookie进行操作。通过<strong>request.getCookie()获取客户端提交的所有Cookie</strong>（以Cookie[]数组形式返回），<strong>通过response.addCookie(Cookiecookie)向客户端设置Cookie。</strong></p> <p align="left">Cookie对象使用key-value属性对的形式保存用户状态，一个Cookie对象保存一个属性对，一个request或者response同时使用多个Cookie。因为Cookie类位于包javax.servlet.http.<sub>*</sub>下面，所以JSP中不需要import该类。</p> <p align="left"><br /> </p> <p align="left"><strong>1.1.3&nbsp; Cookie的不可跨域名性</strong></p> <p align="left">很多网站都会使用Cookie。例如，Google会向客户端颁发Cookie，Baidu也会向客户端颁发Cookie。那浏览器访问Google会不会也携带上Baidu颁发的Cookie呢？或者Google能不能修改Baidu颁发的Cookie呢？</p> <p align="left">答案是否定的。<span style="color:#FF0000"><strong>Cookie具有不可跨域名性</strong>。</span>根据Cookie规范，浏览器访问Google只会携带Google的Cookie，而不会携带Baidu的Cookie。Google也只能操作Google的Cookie，而不能操作Baidu的Cookie。</p> <p align="left">Cookie在客户端是由浏览器来管理的。浏览器能够保证Google只会操作Google的Cookie而不会操作 Baidu的Cookie，从而保证用户的隐私安全。浏览器判断一个网站是否能操作另一个网站Cookie的依据是域名。Google与Baidu的域名 不一样，因此Google不能操作Baidu的Cookie。</p> <p align="left">需要注意的是，虽然网站images.google.com与网站www.google.com同属于Google，但是域名不一样，二者同样不能互相操作彼此的Cookie。</p> <p align="left"><br /> </p> <p align="left">注意：用户登录网站www.google.com之后会发现访问images.google.com时登录信息仍然有效，而普通的Cookie是做不到的。这是因为Google做了特殊处理。本章后面也会对Cookie做类似的处理。</p> <p align="left"><br /> </p> <p align="left"><strong>1.1.4&nbsp; Unicode编码：保存中文</strong></p> <p align="left">中文与英文字符不同，<strong>中文属于Unicode字符，在内存中占4个字符，而英文属于ASCII字符，内存中只占2个字节</strong>。Cookie中使用Unicode字符时需要对Unicode字符进行编码，否则会乱码。</p> <p align="left"><br /> </p> <p align="left">提示：Cookie中保存中文只能编码。一般使用UTF-8编码即可。不推荐使用GBK等中文编码，因为浏览器不一定支持，而且JavaScript也不支持GBK编码。</p> <p align="left"><br /> </p> <p align="left"><strong>1.1.5&nbsp; BASE64编码：保存二进制图片</strong></p> <p align="left">Cookie不仅可以使用ASCII字符与Unicode字符，还可以使用二进制数据。例如在Cookie中使用数字证书，提供安全度。使用二进制数据时也需要进行编码。</p> <p align="left">%注意：本程序仅用于展示Cookie中可以存储二进制内容，并不实用。由于浏览器每次请求服务器都会携带Cookie，因此Cookie内容不宜过多，否则影响速度。Cookie的内容应该少而精。</p> <p align="left"><br /> </p> <p align="left"><strong>1.1.6&nbsp; 设置Cookie的所有属性</strong></p> <p align="left">除了name与value之外，Cookie还具有其他几个常用的属性。每个属性对应一个getter方法与一个setter方法。Cookie类的所有属性如表1.1所示。</p> <p align="left">表1.1&nbsp; Cookie常用属性</p> <table border="0" cellpadding="0" cellspacing="0"> <tbody> <tr> <td> <p align="center">属&nbsp; 性&nbsp; 名</p> </td> <td> <p align="center">描&nbsp;&nbsp;&nbsp; 述</p> </td> </tr> <tr> <td> <p>String name</p> </td> <td> <p>该Cookie的名称。Cookie一旦创建，名称便不可更改</p> </td> </tr> <tr> <td> <p>Object value</p> </td> <td> <p>该Cookie的值。如果值为Unicode字符，需要为字符编码。如果值为二进制数据，则需要使用BASE64编码</p> </td> </tr> <tr> <td> <p><strong>int maxAge</strong></p> </td> <td> <p><strong>该Cookie失效的时间，单位秒。如果为正数，则该Cookie在maxAge秒之后失效。如果为负数，该Cookie为临时Cookie，关闭浏览器即失效，浏览器也不会以任何形式保存该Cookie。如果为0，表示删除该Cookie。默认为&#8211;1</strong></p> </td> </tr> <tr> <td> <p>boolean secure</p> </td> <td> <p>该Cookie是否仅被使用安全协议传输。安全协议。安全协议有HTTPS，SSL等，在网络上传输数据之前先将数据加密。默认为false</p> </td> </tr> <tr> <td> <p>String path</p> </td> <td> <p>该Cookie的使用路径。如果设置为&#8220;/sessionWeb/&#8221;，则只有contextPath为&#8220;/sessionWeb&#8221;的程序可以访问该Cookie。如果设置为&#8220;/&#8221;，则本域名下contextPath都可以访问该Cookie。注意最后一个字符必须为&#8220;/&#8221;</p> </td> </tr> <tr> <td> <p>String domain</p> </td> <td> <p>可以访问该Cookie的域名。如果设置为&#8220;.google.com&#8221;，则所有以&#8220;google.com&#8221;结尾的域名都可以访问该Cookie。注意第一个字符必须为&#8220;.&#8221;</p> </td> </tr> <tr> <td> <p>String comment</p> </td> <td> <p>该Cookie的用处说明。浏览器显示Cookie信息的时候显示该说明</p> </td> </tr> <tr> <td> <p>int version</p> </td> <td> <p>该Cookie使用的版本号。0表示遵循Netscape的Cookie规范，1表示遵循W3C的RFC 2109规范</p> </td> </tr> </tbody> </table> <p align="left"><strong><br /> </strong></p> <p align="left"><strong>1.1.7&nbsp; Cookie的有效期</strong></p> <p align="left">Cookie的maxAge决定着Cookie的有效期，单位为秒（Second）。Cookie中通过getMaxAge()方法与setMaxAge(int maxAge)方法来读写maxAge属性。</p> <p align="left">如果maxAge属性为正数，则表示该Cookie会在maxAge秒之后自动失效。浏览器会将maxAge为正数的 Cookie持久化，即写到对应的Cookie文件中。无论客户关闭了浏览器还是电脑，只要还在maxAge秒之前，登录网站时该Cookie仍然有效。 下面代码中的Cookie信息将永远有效。</p> <p align="left"><br /> </p> <p align="left"><strong>Cookie cookie = new Cookie("username","helloweenvsfei");&nbsp;&nbsp; // 新建Cookie</strong></p> <p align="left"><strong>cookie.setMaxAge(Integer.MAX_VALUE);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 设置生命周期为MAX_VALUE</strong></p> <p align="left"><strong>response.addCookie(cookie); &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 输出到客户端</strong></p> <p align="left"><strong><br /> </strong></p> <p align="left">如果maxAge为负数，则表示该Cookie仅在本浏览器窗口以及本窗口打开的子窗口内有效，关闭窗口后该 Cookie即失效。maxAge为负数的Cookie，为临时性Cookie，不会被持久化，不会被写到Cookie文件中。Cookie信息保存在浏 览器内存中，因此关闭浏览器该Cookie就消失了。Cookie默认的maxAge值为&#8211;1。</p> <p align="left">如果maxAge为0，则表示删除该Cookie。Cookie机制没有提供删除Cookie的方法，因此通过设置该Cookie即时失效实现删除Cookie的效果。失效的Cookie会被浏览器从Cookie文件或者内存中删除，</p> <p align="left"><br /> </p> <p align="left">例如：</p> <p align="left">Cookie cookie = new Cookie("username","helloweenvsfei");&nbsp;&nbsp; // 新建Cookie</p> <p align="left">cookie.setMaxAge(0);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 设置生命周期为0，不能为负数</p> <p align="left">response.addCookie(cookie); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 必须执行这一句</p> <p align="left"><br /> </p> <p align="left">response对象提供的Cookie操作方法只有一个添加操作add(Cookie cookie)。</p> <p align="left">要想修改Cookie只能使用一个同名的Cookie来覆盖原来的Cookie，达到修改的目的。删除时只需要把maxAge修改为0即可。</p> <p align="left"><br /> </p> <p align="left">注意：从客户端读取Cookie时，包括maxAge在内的其他属性都是不可读的，也不会被提交。浏览器提交Cookie时只会提交name与value属性。maxAge属性只被浏览器用来判断Cookie是否过期。</p> <p align="left"><br /> </p> <p align="left"><strong>1.1.8&nbsp; Cookie的修改、删除</strong></p> <p align="left">Cookie并不提供修改、删除操作。如果要修改某个Cookie，只需要新建一个同名的Cookie，添加到response中覆盖原来的Cookie。</p> <p align="left">如果要删除某个Cookie，只需要新建一个同名的Cookie，并将maxAge设置为0，并添加到response中覆盖原来的Cookie。注意是0而不是负数。负数代表其他的意义。读者可以通过上例的程序进行验证，设置不同的属性。</p> <p align="left"><br /> </p> <p align="left">注意：修改、删除Cookie时，新建的Cookie除value、maxAge之外的所有属性，例如name、path、domain等，都要与原Cookie完全一样。否则，浏览器将视为两个不同的Cookie不予覆盖，导致修改、删除失败。</p> <p align="left"><br /> </p> <p align="left"><strong>1.1.9&nbsp; Cookie的域名</strong></p> <p align="left">Cookie是不可跨域名的。域名www.google.com颁发的Cookie不会被提交到域名www.baidu.com去。这是由Cookie的隐私安全机制决定的。隐私安全机制能够禁止网站非法获取其他网站的Cookie。</p> <p align="left">正常情况下，同一个一级域名下的两个二级域名如www.helloweenvsfei.com和 images.helloweenvsfei.com也不能交互使用Cookie，因为二者的域名并不严格相同。如果想所有 helloweenvsfei.com名下的二级域名都可以使用该Cookie，需要设置Cookie的domain参数，例如：</p> <p align="left">Cookie cookie = new Cookie("time","20080808"); // 新建Cookie</p> <p align="left">cookie.setDomain(".helloweenvsfei.com");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 设置域名</p> <p align="left">cookie.setPath("/");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 设置路径</p> <p align="left">cookie.setMaxAge(Integer.MAX_VALUE);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 设置有效期</p> <p align="left">response.addCookie(cookie); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 输出到客户端</p> <p align="left"><br /> </p> <p align="left">读者可以修改本机C:\WINDOWS\system32\drivers\etc下的hosts文件来配置多个临时域名，然后使用setCookie.jsp程序来设置跨域名Cookie验证domain属性。</p> <p align="left">注意：domain参数必须以点(".")开始。另外，name相同但domain不同的两个Cookie是两个不同的Cookie。如果想要两个域名完全不同的网站共有Cookie，可以生成两个Cookie，domain属性分别为两个域名，输出到客户端。</p> <p align="left"><br /> </p> <p align="left"><strong>1.1.10&nbsp; Cookie的路径</strong></p> <p align="left">domain属性决定运行访问Cookie的域名，而path属性决定允许访问Cookie的路径（ContextPath）。例如，如果只允许/sessionWeb/下的程序使用Cookie，可以这么写：</p> <p align="left">Cookie cookie = new Cookie("time","20080808"); &nbsp;&nbsp;&nbsp; // 新建Cookie</p> <p align="left">cookie.setPath("/session/");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 设置路径</p> <p align="left">response.addCookie(cookie); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 输出到客户端</p> <p align="left">设置为&#8220;/&#8221;时允许所有路径使用Cookie。path属性需要使用符号&#8220;/&#8221;结尾。name相同但domain相同的两个Cookie也是两个不同的Cookie。</p> <p align="left"><br /> </p> <p align="left">注意：页面只能获取它属于的Path的Cookie。例如/session/test/a.jsp不能获取到路径为/session/abc/的Cookie。使用时一定要注意。</p> <p align="left"><br /> </p> <p align="left"><strong>1.1.11&nbsp; Cookie的安全属性</strong></p> <p align="left">HTTP协议不仅是无状态的，而且是不安全的。使用HTTP协议的数据不经过任何加密就直接在网络上传播，有被截获的可 能。使用HTTP协议传输很机密的内容是一种隐患。如果不希望Cookie在HTTP等非安全协议中传输，可以设置Cookie的secure属性为 true。浏览器只会在HTTPS和SSL等安全协议中传输此类Cookie。下面的代码设置secure属性为true：</p> <p align="left"><br /> </p> <p align="left"><strong>Cookie cookie = new Cookie("time", "20080808"); // 新建Cookie</strong></p> <p align="left"><strong>cookie.setSecure(true); &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 设置安全属性</strong></p> <p align="left"><strong>response.addCookie(cookie); &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 输出到客户端</strong></p> <p align="left"><strong><br /> </strong></p> <p align="left">提示：secure属性并不能对Cookie内容加密，因而不能保证绝对的安全性。如果需要高安全性，<strong>需要在程序中对Cookie内容加密、解密，以防泄密。</strong></p> <p align="left"><strong><br /> </strong></p> <p align="left"><strong>1.1.12&nbsp; JavaScript操作Cookie</strong></p> <p align="left">Cookie是保存在浏览器端的，因此浏览器具有操作Cookie的先决条件。浏览器可以使用脚本程序如 JavaScript或者VBScript等操作Cookie。这里以JavaScript为例介绍常用的Cookie操作。例如下面的代码会输出本页面 所有的Cookie。</p> <p align="left">&lt;script&gt;document.write(document.cookie);&lt;/script&gt;</p> <p align="left">由于JavaScript能够任意地读写Cookie，有些好事者便想使用JavaScript程序去窥探用户在其他网 站的Cookie。不过这是徒劳的，W3C组织早就意识到JavaScript对Cookie的读写所带来的安全隐患并加以防备了，W3C标准的浏览器会 阻止JavaScript读写任何不属于自己网站的Cookie。换句话说，A网站的JavaScript程序读写B网站的Cookie不会有任何结果。</p> <p align="left"><br /> </p> <p align="left"><strong>1.1.13&nbsp; 案例：永久登录</strong></p> <p align="left">如果用户是在自己家的电脑上上网，登录时就可以记住他的登录信息，下次访问时不需要再次登录，直接访问即可。实现方法是<strong>把登录信息如账号、密码等保存在Cookie中，并控制Cookie的有效期，下次访问时再验证Cookie中的登录信息即可。</strong></p> <p align="left">保存登录信息有多种方案。最直接的是把用户名与密码都保持到Cookie中，下次访问时检查Cookie中的用户名与密码，与数据库比较。这是<strong>一种比较危险的选择，一般不把密码等重要信息保存到Cookie中</strong>。</p> <p align="left">还有<span style="color:#FF0000"><strong>一种方案是把密码加密后保存到Cookie中，下次访问时解密并与数据库比较</strong>。</span>这种方案略微安全一些。如果不希望保存密码，还可以把登录的时间戳保存到Cookie与数据库中，到时只验证用户名与登录时间戳就可以了。</p> <p align="left">这几种方案验证账号时都要查询数据库。</p> <p align="left"><span style="color:#FF0000">本例将采用另一种方案，只在登录时查询一次数据库，以后访问验证登录信息时不再查询数据库。实现方式是<strong>把账号按照一定的规则加密后，连同账号一块保存到Cookie中。下次访问时只需要判断账号的加密规则是否正确即可</strong>。</span>本例把账号保存到名为account的Cookie中，把账号连同密钥用MD1算法加密后保存到名为ssid的Cookie中。验证时验证Cookie中的账号与密钥加密后是否与Cookie中的ssid相等。相关代码如下：</p> <p align="left">代码1.8&nbsp;loginCookie.jsp</p> <p align="left">&lt;%@ page language="java"pageEncoding="UTF-8" isErrorPage="false" %&gt;</p> <p align="left">&lt;%! &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // JSP方法</p> <p align="left">&nbsp;&nbsp;&nbsp; private static final String KEY =":cookie@helloweenvsfei.com";<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 密钥&nbsp;</p> <p align="left">&nbsp;&nbsp;&nbsp; public final static String calcMD1(Stringss) { // MD1 加密算法</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; String s = ss==null ?"" : ss;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 若为null返回空</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; char hexDigits[] = { '0','1', '2', '3', '4', '1', '6', '7', '8', '9',<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; 'a', 'b', 'c', 'd', 'e', 'f' };&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 字典</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; try {</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; byte[] strTemp =s.getBytes();&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 获取字节</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; MessageDigestmdTemp = MessageDigest.getInstance("MD1"); // 获取MD1</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;mdTemp.update(strTemp);&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 更新数据</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; byte[] md =mdTemp.digest(); &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 加密</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; int j =md.length;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 加密后的长度</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; char str[] = newchar[j <sub>*</sub> 2];&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 新字符串数组</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; int k =0;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 计数器k</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; for (int i = 0; i&lt; j; i++) {&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 循环输出</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; byte byte0 =md[i];</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; str[k++] =hexDigits[byte0 &gt;&gt;&gt; 4 &amp; 0xf];</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; str[k++] =hexDigits[byte0 &amp; 0xf];</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return newString(str);&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 加密后字符串</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; } catch (Exception e){return null; }</p> <p align="left">&nbsp;&nbsp;&nbsp; }</p> <p align="left">%&gt;</p> <p align="left">&lt;%</p> <p align="left">&nbsp;&nbsp;&nbsp;request.setCharacterEncoding("UTF-8");&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 设置request编码</p> <p align="left">&nbsp;&nbsp;&nbsp; response.setCharacterEncoding("UTF-8");&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 设置response编码</p> <p align="left">&nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; String action =request.getParameter("action"); // 获取action参数</p> <p align="left">&nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; if("login".equals(action)){&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 如果为login动作</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; String account =request.getParameter("account");<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 获取account参数</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; String password =request.getParameter("password");<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 获取password参数</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; int timeout = newInteger(request.getParameter("timeout"));<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 获取timeout参数</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; String ssid =calcMD1(account + KEY); // 把账号、密钥使用MD1加密后保存</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; CookieaccountCookie = new Cookie("account", account);<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 新建Cookie</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;accountCookie.setMaxAge(timeout);&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 设置有效期</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Cookie ssidCookie =new Cookie("ssid", ssid);&nbsp;&nbsp; // 新建Cookie</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;ssidCookie.setMaxAge(timeout);&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 设置有效期</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;response.addCookie(accountCookie);&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 输出到客户端</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;response.addCookie(ssidCookie); &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 输出到客户端</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 重新请求本页面，参数中带有时间戳，禁止浏览器缓存页面内容</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;response.sendRedirect(request.getRequestURI() + "?" + System.<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; currentTimeMillis());</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return;</p> <p align="left">&nbsp;&nbsp;&nbsp; }</p> <p align="left">&nbsp;&nbsp;&nbsp; elseif("logout".equals(action)){&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 如果为logout动作</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; CookieaccountCookie = new Cookie("account", "");<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 新建Cookie，内容为空</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;accountCookie.setMaxAge(0); &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 设置有效期为0，删除</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Cookie ssidCookie =new Cookie("ssid", ""); // 新建Cookie，内容为空</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;ssidCookie.setMaxAge(0);&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 设置有效期为0，删除</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;response.addCookie(accountCookie);&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;// 输出到客户端</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;response.addCookie(ssidCookie); &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 输出到客户端</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; //重新请求本页面，参数中带有时间戳，禁止浏览器缓存页面内容</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;response.sendRedirect(request.getRequestURI() + "?" + System.<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; currentTimeMillis());</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return;</p> <p align="left">&nbsp;&nbsp;&nbsp; }</p> <p align="left">&nbsp;&nbsp;&nbsp; boolean login = false;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 是否登录</p> <p align="left">&nbsp;&nbsp;&nbsp; String account = null;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 账号</p> <p align="left">&nbsp;&nbsp;&nbsp; String ssid = null; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // SSID标识</p> <p align="left">&nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; if(request.getCookies() !=null){&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 如果Cookie不为空</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; for(Cookie cookie :request.getCookies()){&nbsp; // 遍历Cookie</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if(cookie.getName().equals("account"))&nbsp; // 如果Cookie名为<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; account</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; account = cookie.getValue();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 保存account内容</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;if(cookie.getName().equals("ssid")) // 如果为SSID</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ssid = cookie.getValue();&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 保存SSID内容</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</p> <p align="left">&nbsp;&nbsp;&nbsp; }</p> <p align="left">&nbsp;&nbsp;&nbsp; if(account != null &amp;&amp; ssid !=null){&nbsp;&nbsp;&nbsp; // 如果account、SSID都不为空</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; login =ssid.equals(calcMD1(account + KEY));<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 如果加密规则正确, 则视为已经登录</p> <p align="left">&nbsp;&nbsp;&nbsp; }</p> <p align="left">%&gt;</p> <p align="left">&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN"&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;legend&gt;&lt;%= login ? "欢迎您回来" : "请先登录"%&gt;&lt;/legend&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;% if(login){%&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 欢迎您, ${ cookie.account.value }. &amp;nbsp;&amp;nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;a href="${ pageContext.request.requestURI }?action=logout"&gt;<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; 注销&lt;/a&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;% } else {%&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;formaction="${ pageContext.request.requestURI }?action=login"<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; method="post"&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;table&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;&lt;td&gt;账号： &lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;td&gt;&lt;input type="text"name="account" style="width:<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 200px; "&gt;&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;&lt;td&gt;密码： &lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;td&gt;&lt;inputtype="password" name="password"&gt;&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;td&gt;有效期： &lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;td&gt;&lt;inputtype="radio" name="timeout" value="-1"<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; checked&gt; 关闭浏览器即失效 &lt;br/&gt; &lt;input type="radio" <br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; name="timeout" value="&lt;%= 30 <sub>*</sub>24 <sub>*</sub> 60 <sub> *</sub> 60 %&gt;"&gt; 30天<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 内有效 &lt;br/&gt;&lt;input type="radio" name="timeout" value= <br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "&lt;%= Integer.MAX_VALUE %&gt;"&gt; 永久有效 &lt;br/&gt; &lt;/td&gt; &lt;/tr&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;&lt;td&gt;&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;td&gt;&lt;input type="submit"value=" 登&nbsp; 录 " class= <br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "button"&gt;&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&lt;/table&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/form&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;% } %&gt;</p> <p align="left">登录时可以选择登录信息的有效期：关闭浏览器即失效、30天内有效与永久有效。通过设置Cookie的age属性来实现，注意观察代码。运行效果如图1.7所示。</p> <p align="left"><img src="http://hi.csdn.net/attachment/201111/9/0_1320832572fkE6.gif" alt="" /><br /> </p> <p align="left">图1.7&nbsp; 永久登录</p> <p align="left"><span style="color:#FF0000">提示：该加密机制中最重要的部分为算法与密钥。由于MD1算法的不可逆性，即使用户知道了账号与加密后的字符串，也不可能解密得到密钥。因此，只要保管好密钥与算法，该机制就是安全的。</span></p> <p align="left"><br /> </p> <p align="left"><strong>1.2&nbsp; Session机制</strong></p> <p align="left">除了使用Cookie，Web应用程序中还经常使用Session来记录客户端状态。<strong>Session是服务器端使用的一种记录客户端状态的机制</strong>，使用上比Cookie简单一些，相应的也<strong>增加了服务器的存储压力</strong>。</p> <p align="left"><strong>1.2.1&nbsp; 什么是Session</strong></p> <p align="left"><span style="color:#FF0000">Session是另一种记录客户状态的机制，不同的是Cookie保存在客户端浏览器中，而Session保存在服务器上。客户端浏览器访问服务器的时候，服务器把客户端信息以某种形式记录在服务器上。这就是Session。</span>客户端浏览器再次访问时只需要从该Session中查找该客户的状态就可以了。</p> <p align="left"><span style="color:#FF0000">如果说<strong>Cookie机制是通过检查客户身上的&#8220;通行证&#8221;来确定客户身份的话，那么Session机制就是通过检查服务器上的&#8220;客户明细表&#8221;来确认客户身份。Session相当于程序在服务器上建立的一份客户档案，客户来访的时候只需要查询客户档案表就可以了。</strong></span></p> <p align="left"><strong>1.2.2&nbsp; 实现用户登录</strong></p> <p align="left">Session对应的类为javax.servlet.http.HttpSession类。每个来访者对应一个Session对象，所有该客户的状态信息都保存在这个Session对象里。<strong>Session对象是在客户端第一次请求服务器的时候创建的</strong>。 Session也是一种key-value的属性对，通过getAttribute(Stringkey)和setAttribute(String  key，Objectvalue)方法读写客户状态信息。Servlet里通过request.getSession()方法获取该客户的 Session，</p> <p align="left">例如：</p> <p align="left">HttpSession session = request.getSession(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 获取Session对象</p> <p align="left">session.setAttribute("loginTime", new Date());&nbsp;&nbsp;&nbsp;&nbsp; // 设置Session中的属性</p> <p align="left">&nbsp;&nbsp;&nbsp; </p> <p align="left">out.println("登录时间为：" +(Date)session.getAttribute("loginTime"));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 获取Session属性</p> <p align="left">request还可以使用getSession(boolean  create)来获取Session。区别是如果该客户的Session不存在，request.getSession()方法会返回null，而 getSession(true)会先创建Session再将Session返回。</p> <p align="left">Servlet中必须使用request来编程式获取HttpSession对象，而JSP中内置了Session隐藏 对象，可以直接使用。如果使用声明了&lt;%@page session="false"  %&gt;，则Session隐藏对象不可用。下面的例子使用Session记录客户账号信息。</p> <p align="left">源代码如下：</p> <p align="left">代码1.9&nbsp; session.jsp</p> <p align="left">&lt;%@ page language="java" pageEncoding="UTF-8"%&gt;</p> <p align="left">&lt;jsp:directive.page import="com.helloweenvsfei.sessionWeb.bean.Person"/&gt;</p> <p align="left">&lt;jsp:directive.page import="java.text.SimpleDateFormat"/&gt;</p> <p align="left">&lt;jsp:directive.page import="java.text.DateFormat"/&gt;</p> <p align="left">&lt;jsp:directive.page import="java.util.Date"/&gt;</p> <p align="left">&lt;%!</p> <p align="left">&nbsp;&nbsp;&nbsp; DateFormat dateFormat = newSimpleDateFormat("yyyy-MM-dd");&nbsp;&nbsp;&nbsp; &nbsp; &nbsp;&nbsp; // 日期格式化器</p> <p align="left">%&gt;</p> <p align="left">&lt;%</p> <p align="left">&nbsp;&nbsp;&nbsp; response.setCharacterEncoding("UTF-8");&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 设置request编码</p> <p align="left">&nbsp;&nbsp;&nbsp; Person[] persons =</p> <p align="left">&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br /> </p> <p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 基础数据，保存三个人的信息</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; new Person("Liu Jinghua","password1", 34, dateFormat.parse<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ("1982-01-01")),</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; new Person("Hello Kitty","hellokitty", 23, dateFormat.parse<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ("1984-02-21")),</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; new Person("Garfield", "garfield_pass",23, dateFormat.parse<br /> &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; ("1994-09-12")),</p> <p align="left">&nbsp;&nbsp;&nbsp;&nbsp; };</p> <p align="left">&nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; String message = "";&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 要显示的消息</p> <p align="left">&nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; if(request.getMethod().equals("POST"))</p> <p align="left">&nbsp;&nbsp;&nbsp; { <br /> </p> <p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 如果是POST登录&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; for(Person person :persons)</p> <p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br /> </p> <p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 遍历基础数据，验证账号、密码</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 如果用户名正确且密码正确</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;  &nbsp;&nbsp;&nbsp;if(person.getName().equalsIgnoreCase(request.getParameter("username"))&amp;&amp;person.getPassword().equals(request.getParameter("password")))</p> <p align="left">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 登录成功，设置将用户的信息以及登录时间保存到Session</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; session.setAttribute("person", person);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 保存登录的Person</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; session.setAttribute("loginTime", new Date());&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; // 保存登录的时间&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.sendRedirect(request.getContextPath() + "/welcome.jsp");</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; </p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; message = "用户名密码不匹配，登录失败。";&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // 登录失败</p> <p align="left">&nbsp;&nbsp;&nbsp; }</p> <p align="left">%&gt;</p> <p align="left">&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01Transitional//EN"&gt;</p> <p align="left">&lt;html&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; // ... HTML代码为一个FORM表单，代码略，请看随书光盘</p> <p align="left">&lt;/html&gt;</p> <p align="left"><br /> </p> <p align="left">登录界面验证用户登录信息，如果登录正确，就把用户信息以及登录时间保存进Session，然后转到欢迎页面welcome.jsp。welcome.jsp中从Session中获取信息，并将用户资料显示出来。</p> <p align="left">welcome.jsp代码如下：</p> <p align="left">代码1.10&nbsp; welcome.jsp</p> <p align="left">&lt;%@ page language="java" pageEncoding="UTF-8"%&gt;</p> <p align="left">&lt;jsp:directive.pageimport="com.helloweenvsfei.sessionWeb.bean.Person"/&gt;</p> <p align="left">&lt;jsp:directive.page import="java.text.SimpleDateFormat"/&gt;</p> <p align="left">&lt;jsp:directive.page import="java.text.DateFormat"/&gt;</p> <p align="left">&lt;jsp:directive.page import="java.util.Date"/&gt;</p> <p align="left">&lt;%!</p> <p align="left">&nbsp;&nbsp;&nbsp; DateFormat dateFormat = newSimpleDateFormat("yyyy-MM-dd");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 日期格式化器</p> <p align="left">%&gt;</p> <p align="left">&lt;%</p> <p align="left">&nbsp;&nbsp;&nbsp; Person person =(Person)session.getAttribute("person");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 获取登录的person</p> <p align="left">&nbsp;&nbsp;&nbsp; Date loginTime =(Date)session.getAttribute("loginTime");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 获取登录时间</p> <p align="left">%&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; // ... 部分HTML代码略</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;table&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;&lt;td&gt;您的姓名：&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;td&gt;&lt;%= person.getName()%&gt;&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;&lt;td&gt;登录时间：&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;td&gt;&lt;%= loginTime%&gt;&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;&lt;td&gt;您的年龄：&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;td&gt;&lt;%= person.getAge()%&gt;&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;&lt;td&gt;您的生日：&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;td&gt;&lt;%=dateFormat.format(person.getBirthday()) %&gt;&lt;/td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &lt;/table&gt;</p> <p align="left">程序运行效果如图1.8所示。</p> <p align="left"><img src="http://hi.csdn.net/attachment/201111/9/0_13208326838dA8.gif" alt="" height="280" width="614" /><br /> </p> <p align="left">图1.8&nbsp; 使用Session记录用户信息</p> <p align="left">注意程序中Session中直接保存了Person类对象与Date类对象，使用起来要比Cookie方便。</p> <p align="left">当多个客户端执行程序时，服务器会保存多个客户端的Session。获取Session的时候也不需要声明获取谁的Session。<strong>Session机制决定了当前客户只会获取到自己的Session，而不会获取到别人的Session。各客户的Session也彼此独立，互不可见</strong>。</p> <p align="left"><br /> </p> <p align="left">提示<strong>：<span style="color:#FF0000">Session的使用比Cookie方便，但是过多的Session存储在服务器内存中，会对服务器造成压力。</span></strong></p> <p align="left"><strong><br /> </strong></p> <p align="left"><strong>1.2.3&nbsp; Session的生命周期</strong></p> <p align="left"><span style="color:#FF0000">Session保存在服务器端。<strong>为了获得更高的存取速度，服务器一般把Session放在内存里。每个用户都会有一个独立的Session。如果Session内容过于复杂，当大量客户访问服务器时可能会导致内存溢出。因此，Session里的信息应该尽量精简。</strong></span></p> <p align="left"><strong>Session在用户第一次访问服务器的时候自动创建</strong>。需要注意只有访问JSP、Servlet等程序时才会创建Session，只访问HTML、IMAGE等静态资源并不会创建Session。如果尚未生成Session，也可以使用request.getSession(true)强制生成Session。</p> <p align="left"><strong><span style="color:#FF0000">S</span><span style="color:#FF0000">ession生成后，只要用户继续访问，服务器就会更新Session的最后访问时间，并维护该Session</span></strong><span style="color:#FF0000">。</span>用户每访问服务器一次，无论是否读写Session，服务器都认为该用户的Session&#8220;活跃（active）&#8221;了一次。</p> <p align="left"><br /> </p> <p align="left"><strong>1.2.4&nbsp; Session的有效期</strong></p> <p align="left">由于会有越来越多的用户访问服务器，因此Session也会越来越多。<span style="color:#FF0000"><strong>为防止内存溢出，服务器会把长时间内没有活跃的Session从内存删除。这个时间就是Session的超时时间。如果超过了超时时间没访问过服务器，Session就自动失效了。</strong></span></p> <p align="left">Session的超时时间为maxInactiveInterval属性，可以通过对应的getMaxInactiveInterval()获取，通过setMaxInactiveInterval(longinterval)修改。</p> <p align="left">Session的超时时间也可以在web.xml中修改。另外，通过调用Session的invalidate()方法可以使Session失效。</p> <p align="left"><br /> </p> <p align="left"><strong>1.2.5&nbsp; Session的常用方法</strong></p> <p align="left">Session中包括各种方法，使用起来要比Cookie方便得多。Session的常用方法如表1.2所示。</p> <p align="left">表1.2&nbsp; HttpSession的常用方法</p> <table border="0" cellpadding="0" cellspacing="0"> <tbody> <tr> <td> <p align="center">方&nbsp; 法&nbsp; 名</p> </td> <td> <p align="center">描&nbsp;&nbsp;&nbsp; 述</p> </td> </tr> <tr> <td> <p>void setAttribute(String attribute, Object value)</p> </td> <td> <p>设置Session属性。value参数可以为任何Java Object。通常为Java Bean。value信息不宜过大</p> </td> </tr> <tr> <td> <p>String getAttribute(String attribute)</p> </td> <td> <p>返回Session属性</p> </td> </tr> <tr> <td> <p>Enumeration getAttributeNames()</p> </td> <td> <p>返回Session中存在的属性名</p> </td> </tr> <tr> <td> <p>void removeAttribute(String attribute)</p> </td> <td> <p>移除Session属性</p> </td> </tr> <tr> <td> <p>String getId()</p> </td> <td> <p>返回Session的ID。该ID由服务器自动创建，不会重复</p> </td> </tr> <tr> <td> <p>long getCreationTime()</p> </td> <td> <p>返回Session的创建日期。返回类型为long，常被转化为Date类型，例如：Date createTime = new Date(session.get CreationTime())</p> </td> </tr> <tr> <td> <p>long getLastAccessedTime()</p> </td> <td> <p>返回Session的最后活跃时间。返回类型为long</p> </td> </tr> <tr> <td> <p>int getMaxInactiveInterval()</p> </td> <td> <p>返回Session的超时时间。单位为秒。超过该时间没有访问，服务器认为该Session失效</p> </td> </tr> <tr> <td> <p>void setMaxInactiveInterval(int second)</p> </td> <td> <p>设置Session的超时时间。单位为秒</p> </td> </tr> <tr> <td> <p>void putValue(String attribute, Object value)</p> </td> <td> <p>不推荐的方法。已经被setAttribute(String attribute, Object Value)替代</p> </td> </tr> <tr> <td> <p>Object getValue(String attribute)</p> </td> <td> <p>不被推荐的方法。已经被getAttribute(String attr)替代</p> </td> </tr> <tr> <td> <p>boolean isNew()</p> </td> <td> <p>返回该Session是否是新创建的</p> </td> </tr> <tr> <td> <p>void invalidate()</p> </td> <td> <p>使该Session失效</p> </td> </tr> </tbody> </table> <p align="left">Tomcat中Session的默认超时时间为20分钟。通过setMaxInactiveInterval(int seconds)修改超时时间。可以修改web.xml改变Session的默认超时时间。例如修改为60分钟：</p> <p align="left">&lt;session-config&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp;&lt;session-timeout&gt;60&lt;/session-timeout&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- 单位：分钟 --&gt;</p> <p align="left">&lt;/session-config&gt;</p> <p align="left"><br /> </p> <p align="left">注意：&lt;session-timeout&gt;参数的单位为分钟，而setMaxInactiveInterval(int s)单位为秒。</p> <p align="left"><br /> </p> <p align="left"><strong>1.2.6&nbsp; Session对浏览器的要求</strong></p> <p align="left">虽然Session保存在服务器，对客户端是透明的，它的正常运行仍然需要客户端浏览器的支持。这是因为Session 需要使用Cookie作为识别标志。HTTP协议是无状态的，Session不能依据HTTP连接来判断是否为同一客户，因此服务器向客户端浏览器发送一 个名为JSESSIONID的Cookie，它的值为该Session的id（也就是HttpSession.getId()的返回值）。Session 依据该Cookie来识别是否为同一用户。</p> <p align="left">该Cookie为服务器自动生成的，它的maxAge属性一般为&#8211;1，表示仅当前浏览器内有效，并且各浏览器窗口间不共享，关闭浏览器就会失效。</p> <p align="left"><span style="color:#FF0000">因此同一机器的两个浏览器窗口访问服务器时，会生成两个不同的Session。但是由浏览器窗口内的链接、脚本等打开的新窗口（也就是说不是双击桌面浏览器图标等打开的窗口）除外。这类子窗口会共享父窗口的Cookie，因此会共享一个Session。</span></p> <p align="left"><br /> </p> <p align="left">注意：<span style="color:#FF0000">新开的浏览器窗口会生成新的Session，但子窗口除外。子窗口会共用父窗口的Session。</span>例如，在链接上右击，在弹出的快捷菜单中选择&#8220;在新窗口中打开&#8221;时，子窗口便可以访问父窗口的Session。</p> <p align="left">如果客户端浏览器将Cookie功能禁用，或者不支持Cookie怎么办？例如，绝大多数的手机浏览器都不支持Cookie。Java Web提供了另一种解决方案：URL地址重写。</p> <p align="left"><br /> </p> <p align="left"><strong>1.2.7&nbsp; URL地址重写</strong></p> <p align="left">URL地址重写是对客户端不支持Cookie的解决方案。URL地址重写的原理是将该用户Session的id信息重写 到URL地址中。服务器能够解析重写后的URL获取Session的id。这样即使客户端不支持Cookie，也可以使用Session来记录用户状态。 HttpServletResponse类提供了encodeURL(Stringurl)实现URL地址重写，例如：</p> <p align="left">&lt;td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &lt;a href="&lt;%=response.encodeURL("index.jsp?c=1&amp;wd=Java") %&gt;"&gt; <br /> &nbsp;&nbsp;&nbsp; Homepage&lt;/a&gt;</p> <p align="left">&lt;/td&gt;</p> <p align="left">该方法会自动判断客户端是否支持Cookie。如果客户端支持Cookie，会将URL原封不动地输出来。如果客户端不支持Cookie，则会将用户Session的id重写到URL中。重写后的输出可能是这样的：</p> <p align="left">&lt;td&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &lt;ahref="index.jsp;jsessionid=0CCD096E7F8D97B0BE608AFDC3E1931E?c=<br /> &nbsp;&nbsp;&nbsp; 1&amp;wd=Java"&gt;Homepage&lt;/a&gt;</p> <p align="left">&lt;/td&gt;</p> <p align="left">即在文件名的后面，在URL参数的前面添加了字符串&#8220;;jsessionid=XXX&#8221;。其中XXX为Session的 id。分析一下可以知道，增添的jsessionid字符串既不会影响请求的文件名，也不会影响提交的地址栏参数。用户单击这个链接的时候会把 Session的id通过URL提交到服务器上，服务器通过解析URL地址获得Session的id。</p> <p align="left">如果是页面重定向（Redirection），URL地址重写可以这样写：</p> <p align="left">&lt;%</p> <p align="left">&nbsp;&nbsp;&nbsp; if(&#8220;administrator&#8221;.equals(userName))</p> <p align="left">&nbsp;&nbsp;&nbsp; {</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;response.sendRedirect(response.encodeRedirectURL(&#8220;administrator.jsp&#8221;));</p> <p align="left">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; return;</p> <p align="left">&nbsp;&nbsp;&nbsp; }</p> <p align="left">%&gt;</p> <p align="left">效果跟response.encodeURL(String url)是一样的：如果客户端支持Cookie，生成原URL地址，如果不支持Cookie，传回重写后的带有jsessionid字符串的地址。</p> <p align="left">对于WAP程序，由于大部分的手机浏览器都不支持Cookie，WAP程序都会采用URL地址重写来跟踪用户会话。比如用友集团的移动商街等。</p> <p align="left"><br /> </p> <p align="left">注意：<span style="color:#FF0000">TOMCAT判断客户端浏览器是否支持Cookie的依据是请求中是否含有Cookie。尽管客户端可能会支持Cookie，但是由于第一次请求时不会携带任何Cookie（因为并无任何Cookie可以携带），URL地址重写后的地址中仍然会带有jsessionid。</span>当第二次访问时服务器已经在浏览器中写入Cookie了，因此URL地址重写后的地址中就不会带有jsessionid了。</p> <p align="left"><br /> </p> <p align="left"><strong>1.2.8&nbsp; Session中禁止使用Cookie</strong></p> <p align="left">既然WAP上大部分的客户浏览器都不支持Cookie，索性禁止Session使用Cookie，统一使用URL地址重写会更好一些。Java Web规范支持通过配置的方式禁用Cookie。下面举例说一下怎样通过配置禁止使用Cookie。</p> <p align="left">打开项目sessionWeb的WebRoot目录下的META-INF文件夹（跟WEB-INF文件夹同级，如果没有则创建），打开context.xml（如果没有则创建），编辑内容如下：</p> <p align="left">代码1.11&nbsp;/META-INF/context.xml</p> <p align="left">&lt;?xml version='1.0' encoding='UTF-8'?&gt;</p> <p align="left">&lt;Context path="/sessionWeb"cookies="false"&gt;</p> <p align="left">&lt;/Context&gt;</p> <p align="left"><br /> </p> <p align="left">或者修改Tomcat全局的conf/context.xml，修改内容如下：</p> <p align="left">代码1.12&nbsp; context.xml</p> <p align="left">&lt;!-- The contents of this file will be loaded for eachweb application --&gt;</p> <p align="left">&lt;Context cookies="false"&gt;</p> <p align="left">&nbsp;&nbsp;&nbsp; &lt;!-- ... 中间代码略 --&gt;</p> <p align="left">&lt;/Context&gt;</p> <p align="left">部署后TOMCAT便不会自动生成名JSESSIONID的Cookie，Session也不会以Cookie为识别标志，而仅仅以重写后的URL地址为识别标志了。</p> <p align="left"><br /> </p> <p align="left">注意：该配置只是禁止Session使用Cookie作为识别标志，并不能阻止其他的Cookie读写。也就是说服务器不会自动维护名为JSESSIONID的Cookie了，但是程序中仍然可以读写其他的Cookie。</p> <p align="left"><br /> </p></div>转自：<div>http://blog.csdn.net/fangaoxin/article/details/6952954</div><img src ="http://www.cnitblog.com/stomic/aggbug/89867.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2014-11-27 11:52 <a href="http://www.cnitblog.com/stomic/archive/2014/11/27/89867.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>理解HTTP session原理及应用</title><link>http://www.cnitblog.com/stomic/archive/2014/09/17/89764.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Wed, 17 Sep 2014 14:01:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2014/09/17/89764.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/89764.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2014/09/17/89764.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/89764.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/89764.html</trackback:ping><description><![CDATA[<div>一、术语session<br /> 在我的经验里，session这个词被滥用的程度大概仅次于transaction，更加有趣的是transaction与session在某些语境下的含义是相同的。<br /> &nbsp;<br /> session，中文经常翻译为会话，其本来的含义是指有始有终的一系列动作/消息，比如打电话时从拿起电话拨号到挂断电话这中间的一系列过程可以称之为 一个session。有时候我们可以看到这样的话&#8220;在一个浏览器会话期间，...&#8221;，这里的会话一词用的就是其本义，是指从一个浏览器窗口打开到关闭这个 期间  &#9312;。最混乱的是&#8220;用户（客户端）在一次会话期间&#8221;这样一句话，它可能指用户的一系列动作（一般情况下是同某个具体目的相关的一系列动作，比如从登录到选购 商品到结账登出这样一个网上购物的过程，有时候也被称为一个transaction），然而有时候也可能仅仅是指一次连接，也有可能是指含义&#9312;，其中的差 别只能靠上下文来推断&#9313;。<br /> &nbsp;<br /> 然而当session一词与网络协议相关联时，它又往往隐含了&#8220;面向连接&#8221;和/或&#8220;保持状态&#8221;这样两个含义，&#8220;面向连接&#8221;指的是在通信双方在通信之前要先 建立一个通信的渠道，比如打电话，直到对方接了电话通信才能开始，与此相对的是写信，在你把信发出去的时候你并不能确认对方的地址是否正确，通信渠道不一 定能建立，但对发信人来说，通信已经开始了。&#8220;保持状态&#8221;则是指通信的一方能够把一系列的消息关联起来，使得消息之间可以互相依赖，比如一个服务员能够认 出再次光临的老顾客并且记得上次这个顾客还欠店里一块钱。这一类的例子有&#8220;一个TCP session&#8221;或者&#8220;一个POP3 session&#8221;&#9314;。<br /> &nbsp;<br /> 而到了web服务器蓬勃发展的时代，session在web开发语境下的语义又有了新的扩展，它的含义是指一类用来在客户端与服务器之间保持状态的解决方 案  &#9315;。有时候session也用来指这种解决方案的存储结构，如&#8220;把xxx保存在session里&#8221;&#9316;。由于各种用于web开发的语言在一定程度上都提供了 对这种解决方案的支持，所以在某种特定语言的语境下，session也被用来指代该语言的解决方案，比如经常把Java里提供的 javax.servlet.http.HttpSession简称为session&#9317;。<br /> &nbsp;<br /> 鉴于这种混乱已不可改变，本文中session一词的运用也会根据上下文有不同的含义，请大家注意分辨。<br /> 在本文中，使用中文&#8220;浏览器会话期间&#8221;来表达含义&#9312;，使用&#8220;session机制&#8221;来表达含义&#9315;，使用&#8220;session&#8221;表达含义&#9316;，使用具体的&#8220;HttpSession&#8221;来表达含义&#9317;<br /> &nbsp;<br /> 二、HTTP协议与状态保持<br /> HTTP协议本身是无状态的，这与HTTP协议本来的目的是相符的，客户端只需要简单的向服务器请求<a href="http://www.2cto.com/soft" target="_blank">下载</a>某些文件，无论是客户端还是服务器都没有必要纪录彼此过去的行为，每一次请求之间都是独立的，好比一个顾客和一个自动售货机或者一个普通的（非会员制）大卖场之间的关系一样。<br /> &nbsp;<br /> 然而聪明（或者贪心？）的人们很快发现如果能够提供一些按需生成的动态信息会使web变得更加有用，就像给有线电视加上点播功能一样。这种需求一方面迫使 HTML逐步添加了表单、脚本、DOM等客户端行为，另一方面在服务器端则出现了CGI规范以响应客户端的动态请求，作为传输载体的HTTP协议也添加了 文件上载、cookie这些特性。其中cookie的作用就是为了解决HTTP协议无状态的缺陷所作出的努力。至于后来出现的session机制则是又一 种在客户端与服务器之间保持状态的解决方案。<br /> &nbsp;<br /> 让我们用几个例子来描述一下cookie和session机制之间的区别与联系。笔者曾经常去的一家咖啡店有喝5杯咖啡免费赠一杯咖啡的优惠，然而一次性消费5杯咖啡的机会微乎其微，这时就需要某种方式来纪录某位顾客的消费数量。想象一下其实也无外乎下面的几种方案：<br /> 1、该店的店员很厉害，能记住每位顾客的消费数量，只要顾客一走进咖啡店，店员就知道该怎么对待了。这种做法就是协议本身支持状态。<br /> 2、发给顾客一张卡片，上面记录着消费的数量，一般还有个有效期限。每次消费时，如果顾客出示这张卡片，则此次消费就会与以前或以后的消费相联系起来。这种做法就是在客户端保持状态。<br /> 3、发给顾客一张会员卡，除了卡号之外什么信息也不纪录，每次消费时，如果顾客出示该卡片，则店员在店里的纪录本上找到这个卡号对应的纪录添加一些消费信息。这种做法就是在服务器端保持状态。<br /> &nbsp;<br /> 由于HTTP协议是无状态的，而出于种种考虑也不希望使之成为有状态的，因此，后面两种方案就成为现实的选择。具体来说cookie机制采用的是在客户端 保持状态的方案，而session机制采用的是在服务器端保持状态的方案。同时我们也看到，由于采用服务器端保持状态的方案在客户端也需要保存一个标识， 所以session机制可能需要借助于cookie机制来达到保存标识的目的，但实际上它还有其他选择。<br /> &nbsp;<br /> 三、理解cookie机制<br /> cookie机制的基本原理就如上面的例子一样简单，但是还有几个问题需要解决：&#8220;会员卡&#8221;如何分发；&#8220;会员卡&#8221;的内容；以及客户如何使用&#8220;会员卡&#8221;。<br /> &nbsp;<br /> 正统的cookie分发是通过扩展HTTP协议来实现的，服务器通过在HTTP的响应头中加上一行特殊的指示以提示浏览器按照指示生成相应的cookie。然而纯粹的客户端脚本如JavaScript或者VBScript也可以生成cookie。<br /> &nbsp;<br /> 而cookie的使用是由浏览器按照一定的原则在后台自动发送给服务器的。浏览器检查所有存储的cookie，如果某个cookie所声明的作用范围大于 等于将要请求的资源所在的位置，则把该cookie附在请求资源的HTTP请求头上发送给服务器。意思是麦当劳的会员卡只能在麦当劳的店里出示，如果某家 分店还发行了自己的会员卡，那么进这家店的时候除了要出示麦当劳的会员卡，还要出示这家店的会员卡。<br /> &nbsp;<br /> cookie的内容主要包括：名字，值，过期时间，路径和域。<br /> 其中域可以指定某一个域比如.google.com，相当于总店招牌，比如宝洁公司，也可以指定一个域下的具体某台机器比如www.google.com或者froogle.google.com，可以用飘柔来做比。<br /> 路径就是跟在域名后面的URL路径，比如/或者/foo等等，可以用某飘柔专柜做比。<br /> 路径与域合在一起就构成了cookie的作用范围。<br /> 如果不设置过期时间，则表示这个cookie的生命期为浏览器会话期间，只要关闭浏览器窗口，cookie就消失了。这种生命期为浏览器会话期的 cookie被称为会话cookie。会话cookie一般不存储在硬盘上而是保存在内存里，当然这种行为并不是规范规定的。如果设置了过期时间，浏览器 就会把cookie保存到硬盘上，关闭后再次打开浏览器，这些cookie仍然有效直到超过设定的过期时间。<br /> &nbsp;<br /> 存储在硬盘上的cookie可以在不同的浏览器进程间共享，比如两个IE窗口。而对于保存在内存里的cookie，不同的浏览器有不同的处理方式。对于 IE，在一个打开的窗口上按Ctrl-N（或者从文件菜单）打开的窗口可以与原窗口共享，而使用其他方式新开的IE进程则不能共享已经打开的窗口的内存 cookie；对于Mozilla  Firefox0.8，所有的进程和标签页都可以共享同样的cookie。一般来说是用javascript的window.open打开的窗口会与原窗 口共享内存cookie。浏览器对于会话cookie的这种只认cookie不认人的处理方式经常给采用session机制的web应用<a href="http://www.2cto.com/kf" target="_blank">程序开发</a>者造成很大的困扰。<br /> &nbsp;<br /> 下面就是一个goolge设置cookie的响应头的例子<br /> HTTP/1.1 302 Found<br /> Location: http://www.google.com/intl/zh-CN/<br /> Set-Cookie:  PREF=ID=0565f77e132de138:NW=1:TM=1098082649:LM=1098082649:S=KaeaCFPo49RiA_d8;  expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com<br /> Content-Type: text/html<br /> &nbsp;<br /> 这是使用HTTPLook这个HTTP Sniffer软件来俘获的HTTP通讯纪录的一部分<br /> &nbsp;<br /> 浏览器在再次访问goolge的资源时自动向外发送cookie<br /> &nbsp;<br /> 使用Firefox可以很容易的观察现有的cookie的值<br /> 使用HTTPLook配合Firefox可以很容易的理解cookie的工作原理。<br /> &nbsp;<br /> IE也可以设置在接受cookie前询问<br /> &nbsp;<br /> 这是一个询问接受cookie的对话框。<br /> &nbsp;<br /> 四、理解session机制<br /> session机制是一种服务器端的机制，服务器使用一种类似于散列表的结构（也可能就是使用散列表）来保存信息。<br /> &nbsp;<br /> 当程序需要为某个客户端的请求创建一个session的时候，服务器首先检查这个客户端的请求里是否已包含了一个session标识-  称为session id，如果已包含一个session id则说明以前已经为此客户端创建过session，服务器就按照session  id把这个session检索出来使用（如果检索不到，可能会新建一个），如果客户端请求不包含session  id，则为此客户端创建一个session并且生成一个与此session相关联的session id，session  id的值应该是一个既不会重复，又不容易被找到规律以仿造的字符串，这个session id将被在本次响应中返回给客户端保存。  保存这个session  id的方式可以采用cookie，这样在交互过程中浏览器可以自动的按照规则把这个标识发挥给服务器。一般这个cookie的名字都是类似于 SEEESIONID，而。比如weblogic对于web应用程序生成的 cookie，JSESSIONID=ByOK3vjFD75aPnrF7C2HmdnV6QZcEbzWoWiBYEnLerjQ99zWpBng!- 145788764，它的名字就是JSESSIONID。<br /> &nbsp;<br /> 由于cookie可以被人为的禁止，必须有其他机制以便在cookie被禁止时仍然能够把session  id传递回服务器。经常被使用的一种技术叫做URL重写，就是把session  id直接附加在URL路径的后面，附加方式也有两种，一种是作为URL路径的附加信息，表现形式为http://..... /xxx;jsessionid=ByOK ... 99zWpBng!-145788764<br /> 另一种是作为查询字符串附加在URL后面，表现形式为http://...../xxx?jsessionid=ByOK ... 99zWpBng!-145788764<br /> 这两种方式对于用户来说是没有区别的，只是服务器在解析的时候处理的方式不同，采用第一种方式也有利于把session id的信息和正常程序参数区分开来。<br /> 为了在整个交互过程中始终保持状态，就必须在每个客户端可能请求的路径后面都包含这个session id。<br /> &nbsp;<br /> 另一种技术叫做表单隐藏字段。就是服务器会自动修改表单，添加一个隐藏字段，以便在表单提交时能够把session id传递回服务器。比如下面的表单<br /> &nbsp;<br /> 在被传递给客户端之前将被改写成<br /> &nbsp;<br /> 这种技术现在已较少应用，笔者接触过的很古老的iPlanet6(SunONE应用服务器的前身)就使用了这种技术。<br /> 实际上这种技术可以简单的用对action应用URL重写来代替。<br /> &nbsp;<br /> 在谈论session机制的时候，常常听到这样一种误解&#8220;只要关闭浏览器，session就消失了&#8221;。其实可以想象一下会员卡的例子，除非顾客主动对店家 提出销卡，否则店家绝对不会轻易删除顾客的资料。对session来说也是一样的，除非程序通知服务器删除一个session，否则服务器会一直保留，程 序一般都是在用户做log  off的时候发个指令去删除session。然而浏览器从来不会主动在关闭之前通知服务器它将要关闭，因此服务器根本不会有机会知道浏览器已经关闭，之所 以会有这种错觉，是大部分session机制都使用会话cookie来保存session id，而关闭浏览器后这个session  id就消失了，再次连接服务器时也就无法找到原来的session。如果服务器设置的cookie被保存到硬盘上，或者使用某种手段改写浏览器发出的 HTTP请求头，把原来的session id发送给服务器，则再次打开浏览器仍然能够找到原来的session。<br /> &nbsp;<br /> 恰恰是由于关闭浏览器不会导致session被删除，迫使服务器为seesion设置了一个失效时间，当距离客户端上一次使用session的时间超过这个失效时间时，服务器就可以认为客户端已经停止了活动，才会把session删除以节省存储空间。<br /> &nbsp;<br /> 五、理解javax.servlet.http.HttpSession<br /> HttpSession是Java平台对session机制的实现规范，因为它仅仅是个接口，具体到每个web应用服务器的提供商，除了对规范支持之外，仍然会有一些规范里没有规定的细微差异。这里我们以BEA的Weblogic Server8.1作为例子来演示。<br /> &nbsp;<br /> 首先，Weblogic  Server提供了一系列的参数来控制它的HttpSession的实现，包括使用cookie的开关选项，使用URL重写的开关选项，session持 久化的设置，session失效时间的设置，以及针对cookie的各种设置，比如设置cookie的名字、路径、域，cookie的生存时间等。<br /> &nbsp;<br /> 一般情况下，session都是存储在内存里，当服务器进程被停止或者重启的时候，内存里的session也会被清空，如果设置了session的持久化 特性，服务器就会把session保存到硬盘上，当服务器进程重新启动或这些信息将能够被再次使用，Weblogic  Server支持的持久性方式包括文件、数据库、客户端cookie保存和复制。<br /> &nbsp;<br /> 复制严格说来不算持久化保存，因为session实际上还是保存在内存里，不过同样的信息被复制到各个cluster内的服务器进程中，这样即使某个服务器进程停止工作也仍然可以从其他进程中取得session。<br /> &nbsp;<br /> cookie生存时间的设置则会影响浏览器生成的cookie是否是一个会话cookie。默认是使用会话cookie。有兴趣的可以用它来试验我们在第四节里提到的那个误解。<br /> &nbsp;<br /> cookie的路径对于web应用程序来说是一个非常重要的选项，Weblogic Server对这个选项的默认处理方式使得它与其他服务器有明显的区别。后面我们会专题讨论。<br /> &nbsp;<br /> 关于session的设置参考[5] http://e-docs.bea.com/wls/docs70/webapp/weblogic_xml.html#1036869<br /> &nbsp;<br /> 六、HttpSession常见问题<br /> （在本小节中session的含义为&#9316;和&#9317;的混合）<br /> &nbsp;<br /> 1、session在何时被创建<br /> 一个常见的误解是以为session在有客户端访问时就被创建，然而事实是直到某server端程序调用 HttpServletRequest.getSession(true)这样的语句时才被创建，注意如果JSP没有显示的使用  关闭session，则JSP文件在编译成Servlet时将会自动加上这样一条语句HttpSession session =  HttpServletRequest.getSession(true);这也是JSP中隐含的session对象的来历。<br /> &nbsp;<br /> 由于session会消耗内存资源，因此，如果不打算使用session，应该在所有的JSP中关闭它。<br /> &nbsp;<br /> 2、session何时被删除<br /> 综合前面的讨论，session在下列情况下被删除a.程序调用HttpSession.invalidate();或b.距离上一次收到客户端发送的session id时间间隔超过了session的超时设置;或c.服务器进程被停止（非持久session）<br /> &nbsp;<br /> 3、如何做到在浏览器关闭时删除session<br /> 严格的讲，做不到这一点。可以做一点努力的办法是在所有的客户端页面里使用javascript代码window.oncolose来监视浏览器的关闭动作，然后向服务器发送一个请求来删除session。但是对于浏览器崩溃或者强行杀死进程这些非常规手段仍然无能为力。<br /> &nbsp;<br /> 4、有个HttpSessionListener是怎么回事<br /> 你可以创建这样的listener去监控session的创建和销毁事件，使得在发生这样的事件时你可以做一些相应的工作。注意是session的创建和 销毁动作触发listener，而不是相反。类似的与HttpSession有关的listener还有 HttpSessionBindingListener，HttpSessionActivationListener和 HttpSessionAttributeListener。<br /> &nbsp;<br /> 5、存放在session中的对象必须是可序列化的吗<br /> 不是必需的。要求对象可序列化只是为了session能够在集群中被复制或者能够持久保存或者在必要时server能够暂时把session交换出内存。 在Weblogic  Server的session中放置一个不可序列化的对象在控制台上会收到一个警告。我所用过的某个iPlanet版本如果session中有不可序列化 的对象，在session销毁时会有一个Exception，很奇怪。<br /> &nbsp;<br /> 6、如何才能正确的应付客户端禁止cookie的可能性<br /> 对所有的URL使用URL重写，包括超链接，form的action，和重定向的URL，具体做法参见[6]<br /> http://e-docs.bea.com/wls/docs70/webapp/sessions.html#100770<br /> &nbsp;<br /> 7、开两个浏览器窗口访问应用程序会使用同一个session还是不同的session<br /> 参见第三小节对cookie的讨论，对session来说是只认id不认人，因此不同的浏览器，不同的窗口打开方式以及不同的cookie存储方式都会对这个问题的答案有影响。<br /> &nbsp;<br /> 8、如何防止用户打开两个浏览器窗口操作导致的session混乱<br /> 这个问题与防止表单多次提交是类似的，可以通过设置客户端的令牌来解决。就是在服务器每次生成一个不同的id返回给客户端，同时保存在session里， 客户端提交表单时必须把这个id也返回服务器，程序首先比较返回的id与保存在session里的值是否一致，如果不一致则说明本次操作已经被提交过了。 可以参看《J2EE核心模式》关于表示层模式的部分。需要注意的是对于使用javascript  window.open打开的窗口，一般不设置这个id，或者使用单独的id，以防主窗口无法操作，建议不要再window.open打开的窗口里做修改 操作，这样就可以不用设置。<br /> &nbsp;<br /> 9、为什么在Weblogic Server中改变session的值后要重新调用一次session.setValue<br /> 做这个动作主要是为了在集群环境中提示Weblogic Server session中的值发生了改变，需要向其他服务器进程复制新的session值。<br /> &nbsp;<br /> 10、为什么session不见了<br /> 排除session正常失效的因素之外，服务器本身的可能性应该是微乎其微的，虽然笔者在iPlanet6SP1加若干补丁的Solaris版本上倒也遇 到过；浏览器插件的可能性次之，笔者也遇到过3721插件造成的问题；理论上防火墙或者代理服务器在cookie处理上也有可能会出现问题。<br /> 出现这一问题的大部分原因都是程序的错误，最常见的就是在一个应用程序中去访问另外一个应用程序。我们在下一节讨论这个问题。<br /> &nbsp;<br /> 七、跨应用程序的session共享<br /> &nbsp;<br /> 常常有这样的情况，一个大项目被分割成若干小项目开发，为了能够互不干扰，要求每个小项目作为一个单独的web应用程序开发，可是到了最后突然发现某几个 小项目之间需要共享一些信息，或者想使用session来实现SSO(single sign  on)，在session中保存login的用户信息，最自然的要求是应用程序间能够访问彼此的session。<br /> &nbsp;<br /> 然而按照Servlet规范，session的作用范围应该仅仅限于当前应用程序下，不同的应用程序之间是不能够互相访问对方的session的。各个应 用服务器从实际效果上都遵守了这一规范，但是实现的细节却可能各有不同，因此解决跨应用程序session共享的方法也各不相同。<br /> &nbsp;<br /> 首先来看一下Tomcat是如何实现web应用程序之间session的隔离的，从Tomcat设置的cookie路径来看，它对不同的应用程序设置的 cookie路径是不同的，这样不同的应用程序所用的session  id是不同的，因此即使在同一个浏览器窗口里访问不同的应用程序，发送给服务器的session id也可以是不同的。<br /> &nbsp;<br /> 根据这个特性，我们可以推测Tomcat中session的内存结构大致如下。<br /> &nbsp;<br /> 笔者以前用过的iPlanet也采用的是同样的方式，估计SunONE与iPlanet之间不会有太大的差别。对于这种方式的服务器，解决的思路很简单， 实际实行起来也不难。要么让所有的应用程序共享一个session id，要么让应用程序能够获得其他应用程序的session id。<br /> &nbsp;<br /> iPlanet中有一种很简单的方法来实现共享一个session id，那就是把各个应用程序的cookie路径都设为/（实际上应该是/NASApp，对于应用程序来讲它的作用相当于根）。<br /> &nbsp;<br /> /NASApp<br /> &nbsp;<br /> 需要注意的是，操作共享的session应该遵循一些<a href="http://www.2cto.com/kf" target="_blank">编程</a>约定，比如在session attribute名字的前面加上应用程序的前缀，使得setAttribute("name", "neo")变成setAttribute("app1.name", "neo")，以防止命名空间冲突，导致互相覆盖。<br /> &nbsp;<br /> 在Tomcat中则没有这么方便的选择。在Tomcat版本3上，我们还可以有一些手段来共享session。对于版本4以上的Tomcat，目前笔者尚 未发现简单的办法。只能借助于第三方的力量，比如使用文件、数据库、JMS或者客户端cookie，URL参数或者隐藏字段等手段。<br /> &nbsp;<br /> 我们再看一下Weblogic Server是如何处理session的。<br /> &nbsp;<br /> 从截屏画面上可以看到Weblogic Server对所有的应用程序设置的cookie的路径都是/，这是不是意味着在Weblogic  Server中默认的就可以共享session了呢？然而一个小实验即可证明即使不同的应用程序使用的是同一个session，各个应用程序仍然只能访问 自己所设置的那些属性。这说明Weblogic Server中的session的内存结构可能如下<br /> &nbsp;<br /> 对于这样一种结构，在session机制本身上来解决session共享的问题应该是不可能的了。除了借助于第三方的力量，比如使用文件、数据库、JMS 或者客户端cookie，URL参数或者隐藏字段等手段，还有一种较为方便的做法，就是把一个应用程序的session放到ServletContext  中，这样另外一个应用程序就可以从ServletContext中取得前一个应用程序的引用。示例代码如下，<br /> &nbsp;<br /> 应用程序A<br /> context.setAttribute("appA", session);<br /> &nbsp;<br /> 应用程序B<br /> contextA = context.getContext("/appA");<br /> HttpSession sessionA = (HttpSession)contextA.getAttribute("appA");<br /> &nbsp;<br /> 值得注意的是这种用法不可移植，因为根据ServletContext的JavaDoc，应用服务器可以处于安全的原因对于context.getContext("/appA");返回空值，以上做法在Weblogic Server 8.1中通过。<br /> &nbsp;<br /> 那么Weblogic  Server为什么要把所有的应用程序的cookie路径都设为/呢？原来是为了SSO，凡是共享这个session的应用程序都可以共享认证的信息。一 个简单的实验就可以证明这一点，修改首先登录的那个应用程序的描述符weblogic.xml，把cookie路径修改为/appA访问另外一个应用程序 会重新要求登录，即使是反过来，先访问cookie路径为/的应用程序，再访问修改过路径的这个，虽然不再提示登录，但是登录的用户信息也会丢失。注意做 这个实验时认证方式应该使用FORM，因为<a href="http://www.2cto.com/os/liulanqi/" target="_blank">浏览器</a>和web服务器对basic认证方式有其他的处理方式，第二次请求的认证不是通过session来实现的。具体请参看[7] secion 14.8 Authorization，你可以修改所附的示例程序来做这些试验。<br /> &nbsp;<br /> 八、总结<br /> session机制本身并不复杂，然而其实现和配置上的灵活性却使得具体情况复杂多变。这也要求我们不能把仅仅某一次的经验或者某一个浏览器，服务器的经验当作普遍适用的经验，而是始终需要具体情况具体分析。<br /> &nbsp;<br /> <br /> &nbsp;<br /> session与cookie区别<br /> &nbsp;<br /> Session是由应用服务器维持的一个服务器端的存储空间，用户在连接服务器时，会由服务器生成一个唯一的SessionID,用该SessionID 为标识符来存取服务器端的Session存储空间。而SessionID这一数据则是保存到客户端，用Cookie保存的，用户提交页面时，会将这一 SessionID提交到服务器端，来存取Session数据。这一过程，是不用开发人员干预的。所以一旦客户端禁用Cookie，那么Session也 会失效。<br /> &nbsp;<br /> 服务器也可以通过URL重写的方式来传递SessionID的值，因此不是完全依赖Cookie。如果客户端Cookie禁用，则服务器可以自动通过重写URL的方式来保存Session的值，并且这个过程对程序员透明。<br /> &nbsp;<br /> 可以试一下，即使不写Cookie，在使用request.getCookies();取出的Cookie数组的长度也是1，而这个Cookie的名字就是JSESSIONID，还有一个很长的二进制的字符串，是SessionID的值。<br /> &nbsp;<br /> Cookie是客户端的存储空间，由浏览器来维持。<br /> &nbsp;<br /> 在一些投票之类的场合，我们往往因为公平的原则要求每人只能投一票，在一些WEB开发中也有类似的情况，这时候我们通常会使用COOKIE来实现，例如如下的代码：<br /> &nbsp;<br /> &lt; % cookie[]cookies = request.getCookies();<br /> &nbsp;<br /> if (cookies.lenght == 0 || cookies == null)<br /> &nbsp;<br /> doStuffForNewbie();<br /> &nbsp;<br /> //没有访问过 www.2cto.com<br /> &nbsp;<br /> }<br /> &nbsp;<br /> else<br /> &nbsp;<br /> {<br /> &nbsp;<br /> doStuffForReturnVisitor(); //已经访问过了<br /> &nbsp;<br /> }<br /> &nbsp;<br /> % &gt;<br /> &nbsp;<br /> 这是很浅显易懂的道理，检测COOKIE的存在，如果存在说明已经运行过写入COOKIE的代码了，然而运行以上的代码后，无论何时结果都是执行 doStuffForReturnVisitor()，通过控制面板-Internet选项-设置-察看文件却始终看不到生成的cookie文件，奇怪， 代码明明没有问题，不过既然有cookie，那就显示出来看看。<br /> &nbsp;<br /> cookie[]cookies = request.getCookies();<br /> &nbsp;<br /> if (cookies.lenght == 0 || cookies == null)<br /> &nbsp;<br /> out.println("Has not visited this website");<br /> &nbsp;<br /> }<br /> &nbsp;<br /> else<br /> &nbsp;<br /> {<br /> &nbsp;<br /> for (int i = 0; i &lt; cookie.length; i++)<br /> &nbsp;<br /> {<br /> &nbsp;<br /> out.println("cookie name:" + cookies[ i ].getName() + "cookie value:" +<br /> &nbsp;<br /> cookie[ i ].getValue());<br /> &nbsp;<br /> }<br /> &nbsp;<br /> }<br /> &nbsp;<br /> 运行结果:<br /> &nbsp;<br /> cookie name:JSESSIONID cookie value:KWJHUG6JJM65HS2K6<br /> &nbsp;<br /> 为什么会有cookie呢,大家都知道，http是无状态的协议，客户每次读取web页面时，服务器都打开新的会话，而且服务器也不会自动维护客户的上下 文信息，那么要怎么才能实现网上商店中的购物车呢，session就是一种保存上下文信息的机制，它是针对每一个用户的，变量的值保存在服务器端，通过 SessionID来区分不同的客户,session是以cookie或URL重写为基础的，默认使用cookie来实现，系统会创造一个名为 JSESSIONID的输出cookie，我们叫做session  cookie,以区别persistentcookies,也就是我们通常所说的cookie,注意sessioncookie是存储于浏览器内存中的， 并不是写到硬盘上的，这也就是我们刚才看到的JSESSIONID。<br /> &nbsp;<br /> 我们通常情是看不到JSESSIONID的，但是当我们把浏览器的cookie禁止后，web服务器会采用URL重写的方式传递Sessionid，我们就可以在地址栏看到sessionid=KWJHUG6JJM65HS2K6之类的字符串。<br /> &nbsp;<br /> 明白了原理，我们就可以很容易的分辨出persistent  cookies和sessioncookie的区别了，网上那些关于两者安全性的讨论也就一目了然了，session  cookie针对某一次会话而言，会话结束sessioncookie也就随着消失了，而persistentcookie只是存在于客户端硬盘上的一段 文本（通常是加密的），而且可能会遭到cookie欺骗以及针对cookie的跨站脚本攻击，自然不如session cookie安全了。<br /> &nbsp;<br /> 通常sessioncookie是不能跨窗口使用的，当你新开了一个浏览器窗口进入相同页面时，系统会赋予你一个新的sessionid，这样我们信息共 享的目的就达不到了，此时我们可以先把sessionid保存在persistentcookie中，然后在新窗口中读出来，就可以得到上一个窗口 SessionID了，这样通过session cookie和persistentcookie的结合我们就实现了跨窗口的session  tracking（会话跟踪）。<br /> &nbsp;<br /> 在一些web开发的书中，往往只是简单的把Session和cookie作为两种并列的http传送信息的方式，sessioncookies位于服务器 端，persistentcookie位于客户端，可是session又是以cookie为基础的，明白的两者之间的联系和区别，我们就不难选择合适的技 术来开发webservice了、<br /> 本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/taoff/articles/1921009.<a href="http://www.2cto.com/kf/web/asp/" target="_blank">asp</a>x						</div><img src ="http://www.cnitblog.com/stomic/aggbug/89764.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2014-09-17 22:01 <a href="http://www.cnitblog.com/stomic/archive/2014/09/17/89764.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈HTTP的无状态性 以及 cookie、session</title><link>http://www.cnitblog.com/stomic/archive/2013/12/12/88982.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Thu, 12 Dec 2013 03:27:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2013/12/12/88982.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/88982.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2013/12/12/88982.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/88982.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/88982.html</trackback:ping><description><![CDATA[<div>HTTP是Hyper Text Transfer  Protocol的缩写，顾名思义，这个协议支持着超文本的传输。那么什么是超文本呢？说白了就是使用HTML编写的页面。通常，我们使用客户端浏览器访 问服务器的资源，最常见的URL也是以html为后缀的文件。因此，我们可以说超文本是网络上最主要的资源。 <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  既然HTTP协议的目的在于支持超文本的传输，更加广义一些就是支持资源的传输，那么在客户端浏览器向HTTP服务器发送请求，继而HTTP服务器将相应 的资源发回给客户端这样一个过程中，无论对于客户端还是服务器，都没有必要记录这个过程，因为每一次请求和响应都是相对独立的，就好像你在自动售货机前投 下硬币购买商品一样，谁都不会也不需要记住这样一个交易过程。一般而言，一个URL对应着唯一的超文本，而HTTP服务器也绝对公平公正，不管你是 Michael，还是Jordon，它都会根据接收到的URL请求返回相同的超文本。正是因为这样的唯一性，使得记录用户的行为状态变得毫无意义，所 以，HTTP协议被设计为无状态的连接协议符合它本身的需求。 <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  然而，随着时间的推移，人们发现静态的HTML着实无聊而乏味，增加动态生成的内容才会令Web应用程序变得更加有用。于是乎，HTML的语法在不断膨 胀，其中最重要的是增加了表单（Form）；客户端也增加了诸如脚本处理、DOM处理等功能；对于服务器，则相应的出现了CGI（Common  Gateway  Interface）以处理包含表单提交在内的动态请求。在这种客户端与服务器进行动态交互的Web应用程序出现之后，HTTP无状态的特性严重阻碍了这 些应用程序的实现，毕竟交互是需要承前启后的，简单的购物车程序也要知道用户到底在之前选择了什么商品。于是，两种用于保持HTTP连接状态的技术就应运 而生了，一个是Cookie，而另一个则是Session。 <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  Cookie是通过客户端保持状态的解决方案。从定义上来说，Cookie就是由服务器发给客户端的特殊信息，而这些信息以文本文件的方式存放在客户端， 然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。让我们说得更具体一些：当用户使用浏览器访问一个支持Cookie的网站的时候，用户会提 供包括用户名在内的个人信息并且提交至服务器；接着，服务器在向客户端回传相应的超文本的同时也会发回这些个人信息，当然这些信息并不是存放在HTTP响 应体（Response Body）中的，而是存放于HTTP响应头（Response   Header）；当客户端浏览器接收到来自服务器的响应之后，浏览器会将这些信息存放在一个统一的位置，对于Windows操作系统而言，我们可以从： [系统盘]:\Documents and  Settings\[用户名]\Cookies目录中找到存储的Cookie；自此，客户端再向服务器发送请求的时候，都会把相应的Cookie再次发回 至服务器。而这次，Cookie信息则存放在HTTP请求头（Request Header）了。 <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  有了Cookie这样的技术实现，服务器在接收到来自客户端浏览器的请求之后，就能够通过分析存放于请求头的Cookie得到客户端特有的信息，从而动态 生成与该客户端相对应的内容。通常，我们可以从很多网站的登录界面中看到&#8220;请记住我&#8221;这样的选项，如果你勾选了它之后再登录，那么在下一次访问该网站的时 候就不需要进行重复而繁琐的登录动作了，而这个功能就是通过Cookie实现的。 <br /> &nbsp;&nbsp;&nbsp; <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  与Cookie相对的一个解决方案是Session，它是通过服务器来保持状态的。由于Session这个词汇包含的语义很多，因此需要在这里明确一下 Session的含义。首先，我们通常都会把Session翻译成会话，因此我们可以把客户端浏览器与服务器之间一系列交互的动作称为一个 Session。从这个语义出发，我们会提到Session持续的时间，会提到在Session过程中进行了什么操作等等；其次，Session指的是服 务器端为客户端所开辟的存储空间，在其中保存的信息就是用于保持状态。从这个语义出发，我们则会提到往Session中存放什么内容，如何根据键值从 Session中获取匹配的内容等。 <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;  要使用Session，第一步当然是创建Session了。那么Session在何时创建呢？当然还是在服务器端程序运行的过程中创建的，不同语言实现的 应用程序有不同创建Session的方法，而在Java中是通过调用HttpServletRequest的getSession方法（使用true作为 参数）创建的。在创建了Session的同时，服务器会为该Session生成唯一的Session id，而这个Session  id在随后的请求中会被用来重新获得已经创建的Session；在Session被创建之后，就可以调用Session相关的方法往Session中增加 内容了，而这些内容只会保存在服务器中，发到客户端的只有Session  id；当客户端再次发送请求的时候，会将这个Session id带上，服务器接受到请求之后就会依据Session  id找到相应的Session，从而再次使用之。正式这样一个过程，用户的状态也就得以保持了。有关Session的内容还比较多，在以后的Post中， 我还将继续讲述。 <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 综上所述，HTTP本身是一个无状态的连接协议，为了支持客户端与服务器之间的交互，我们就需要通过不同的技术为交互存储状态，而这些不同的技术就是Cookie和Session了。 <br /> PS： <br /> &nbsp;&nbsp; 1.每个Session都有一个Session&nbsp;&nbsp;  ID,这个ID是存放在哪里的呢？有两种情况，一种是通过Cookie存取（Tomcat里面默认就是如此），还有一种是通过重写Url存取，后面的方法 会在Url上多出一个字符串如sessionid=llkjkl3k22390等等，这样每次重新来过的时候都把Url中的SessionID传一次。而   Cookie的方式就较简单，程序一开始执行，服务器就生成一个SessionID并通过Cookie写在客户端浏览器的缓冲中，当下一次访问的时候，服 务器先检测一下是否有这个Cookie，如果有，取他的ID，如果没有，新生成一个。这就是为什么大家关闭浏览器后，再进去Session已经找不到了， 其实在服务器端Session并没有清空，而是SessionID变了。&nbsp;&nbsp;&nbsp; <br /> &nbsp;  session里面的数据都放在服务器端，通过sessionID保证不会访问错误，服务器端自动对Session进行管理，如果在规定时间内没有访问， 则释放这个Session。因此，当客户关闭浏览器，他用过的Session没有人去访问，就会在一段时间内被释放。&nbsp;&nbsp;&nbsp; <br /> &nbsp;  由上可见，Session是通过一个ID建立客户端和服务器端的联系的，因此，象那些想在浏览器关闭的同时释放Session不可行（除非触发一个页面去 手工释放）就不奇怪了。因为Cookie是可窗口绑在一起的，当浏览器全部关闭，这个cookie也就没有用了，下一次再来，有新生成一个Cookie， 又是一个新的Session了，大家可以试试，把浏览器的Cookie不接收，那样Session就没有用了。&nbsp; <br /> &nbsp;&nbsp;&nbsp;  2.http是无状态的协议，客户每次读取web页面时，服务器都打开新的会话，而且服务器也不会自动维护客户的上下文信息，那么要怎么才能实现网上商店 中的购物车呢，session就是一种保存上下文信息的机制，它是针对每一个用户的，变量的值保存在服务器端，通过SessionID来区分不同的客 户,session是以cookie或URL重写为基础的，默认使用cookie来实现，系统会创造一个名为JSESSIONID的输出cookie，我 们叫做session cookie,以区别persistent cookies,也就是我们通常所说的cookie,注意session   cookie是存储于浏览器内存中的，并不是写到硬盘上的，这也就是我们刚才看到的JSESSIONID，我们通常情是看不到JSESSIONID的，但 是当我们把浏览器的cookie禁止后，web服务器会采用URL重写的方式传递Sessionid，我们就可以在地址栏看到 sessionid=KWJHUG6JJM65HS2K6之类的字符串。 <br /> 明白了原理，我们就可以很容易的分辨出persistent cookies和session  cookie的区别了，网上那些关于两者安全性的讨论也就一目了然了，session cookie针对某一次会话而言，会话结束session  cookie也就随着消失了，而persistent  cookie只是存在于客户端硬盘上的一段文本（通常是加密的），而且可能会遭到cookie欺骗以及针对cookie的跨站脚本攻击，自然不如  session cookie安全了。 <br /> 通常session  cookie是不能跨窗口使用的，当你新开了一个浏览器窗口进入相同页面时，系统会赋予你一个新的sessionid，这样我们信息共享的目的就达不到 了，此时我们可以先把sessionid保存在persistent  cookie中，然后在新窗口中读出来，就可以得到上一个窗口SessionID了，这样通过session cookie和persistent  cookie的结合我们就实现了跨窗口的session tracking（会话跟踪）。 <br /> 在一些web开发的书中，往往只是简单的把Session和cookie作为两种并列的http传送信息的方式，session  cookies位于服务器端，persistent  cookie位于客户端，可是session又是以cookie为基础的，明白的两者之间的联系和区别，我们就不难选择合适的技术来开发web  service了。 </div><img src ="http://www.cnitblog.com/stomic/aggbug/88982.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2013-12-12 11:27 <a href="http://www.cnitblog.com/stomic/archive/2013/12/12/88982.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>User Story 在敏捷开发过程中的应用</title><link>http://www.cnitblog.com/stomic/archive/2011/06/17/74410.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 17 Jun 2011 09:35:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2011/06/17/74410.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/74410.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2011/06/17/74410.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/74410.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/74410.html</trackback:ping><description><![CDATA[<div><h1><span style="line-height: 150%;">1<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="line-height: 150%; font-family: 宋体;">从这里开始</span></h1> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">第一部分我们将快速浏览什么是</span><span style="font-size: 12pt; line-height: 150%;">user stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">以及如何使用，然后将阐述如何编写</span><span style="font-size: 12pt; line-height: 150%;">User Stories；</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">如何通过系统用户模型来定义</span><span style="font-size: 12pt; line-height: 150%;">Stories；</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">当客户自己本身无法前来的时候，如何同那些能够充当客户角色的人一起工作；</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">如何来编写测试用例，来证明你的</span><span style="font-size: 12pt; line-height: 150%;">Stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">已经被成功编写的细节，最后将阐述几条编写好的</span><span style="font-size: 12pt; line-height: 150%;">Story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">的指导建议。</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">当你学完这部分之后，你就可以定义、编写、测试你的</span><span style="font-size: 12pt; line-height: 150%;">Stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">，同时你应该准备去看如何通过</span><span style="font-size: 12pt; line-height: 150%;">User Stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">去进行评估和计划，也就是第二部分的内容。</span></p> <h2><span style="font-size: 15pt; line-height: 150%;">1.1 </span><span style="font-size: 15pt; line-height: 150%;">概述</span></h2> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">软件需求是一个沟通的问题，想得到（或者使用，或者出售）新软件的人，必须和软件的生产者进行沟通。项目的成功与否，依赖于从不同的人那里得到的信息：一方面是客户、用户、分析人员、领域专家以及其他从业务或者组织角度来看待软件的人；另一方面则是技术团队。</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">如果任何一方控制了沟通，那么项目注定会失败。如果业务一方控制，则会要求功能和日期，而不太担心开发人员是否能全部完成或者开发人员是否明白他们的真正要求；如果开发人员控制了沟通，技术术语会代替业务语言，开发人员也失去了通过倾听来了解客户真正需求的机会。</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">我们需要一种方法让大家一起合作，以至于沟通不会被单方控制，并且资源分配中的感情因素和原则问题就变成了双方共同的问题。</span><span style="font-size: 12pt; line-height: 150%;">.</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">当资源分配完全倾向一方的时候，项目就会失败。如果开发人员全权负责（无论怎样都必须在</span><span style="font-size: 12pt; line-height: 150%;">7</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">月 份之前全部做完），他们可能会因为一些附加的功能而牺牲质量，或者只实现部分功能，或者独自制定本该客户或用户参与制定的大量的决定。当客户和用户方全权 负责，项目前期就会出现一个漫长的讨论过程，在这个过程中越来越多的功能被从项目中删除，当软件被交付的时候，甚至实现的功能比删掉的功能少。</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">我们已经知道了我们不能够完美的预言一个软件开发项目。当用户看到软件的初版时，他们会产生一些新的想法，改变一些他们原有的想法，由于软件的不可把握性，开发人员进行时间估算变得非常困难。由于种种原因，我们无法罗列一个完整的</span><span style="font-size: 12pt; line-height: 150%;">PERT</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">图表来显示我们在项目里所必须完成的全部工作。</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">那么，怎么办？</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">我们经常通过手头已经掌握的资料来做决定，会好过在项目初期就做出所有的决定。我们把做决定分散到整个项目过程中。为了做到这一点，我们要确认已经有一个尽早尽多获得相关的资料的程序。</span><span style="font-size: 12pt; line-height: 150%;">User Stories </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">由此而生。</span></p> <h3><span style="font-size: 14pt; line-height: 173%;">1.1.1 </span><span style="font-size: 14pt; line-height: 173%;">User Story </span><span style="font-size: 14pt; line-height: 173%; font-family: 宋体;">是什么？</span></h3> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%;">User story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">是对软件的用户或买主有价值的功能点的描述。</span><span style="font-size: 12pt; line-height: 150%;">User stories </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">由以下三点组成：</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">用来制定计划和作为提醒的一段书面描述</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">用来充实</span><span style="font-size: 12pt; line-height: 150%;">story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">的细节的谈话</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">测试用例，用来表达和记录细节并且能够在</span><span style="font-size: 12pt; line-height: 150%;">story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">实现的时候对进行验证</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">因为</span><span style="font-size: 12pt; line-height: 150%;">User Story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">的描述是通过传统的手写记录在卡片上，所以</span><span style="font-size: 12pt; line-height: 150%;">Ron Jeffies</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">给这三个方面起了很好的名字，</span><span style="font-size: 12pt; line-height: 150%;">Card</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（卡片），</span><span style="font-size: 12pt; line-height: 150%;">Conversation</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（会话），和</span><span style="font-size: 12pt; line-height: 150%;">Confirmation</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（确认）。卡片是</span><span style="font-size: 12pt; line-height: 150%;">story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">最可见的表现形式，但是他不是最重要的。</span><span style="font-size: 12pt; line-height: 150%;">Rachel Davies </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">已经说过，卡片&#8220;重现客户需求场景好于记录它们&#8221;。思考</span><span style="font-size: 12pt; line-height: 150%;">User Stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">的完美方法是：</span><span style="font-size: 12pt; line-height: 150%;">card </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">包含</span><span style="font-size: 12pt; line-height: 150%;">story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">的正文，通过会话得出细节，并记录在测试用例中。</span></p> <p style="margin: auto 0cm; text-indent: 24pt;">User story 的例子，请参见<a href="mk:@MSITStore:D:%5C%E5%B7%A5%E4%BD%9C%E7%A7%AF%E7%B4%AF%5C%E5%85%B6%E4%BB%96%5C%E7%BF%BB%E8%AF%91%5Cuser.stories.applied.for.agile.software.development.chm::/0321205685/ch01lev1sec1.html#ch01sc01#ch01sc01">Story Card 1.1</a></p> <p style="margin: auto 0cm; text-indent: 24pt;">Story Card 1.1&nbsp;是一个写在卡片上的初期的User Story</p>  <p style="margin: auto 0cm; text-indent: 24pt;"><span style="font-size: 12pt; font-family: 宋体;">&nbsp;<a href="http://space.flash8.net/space/batch.download.php?aid=70371" target="_blank"><img src="http://space.flash8.net/space/attachments/2007/10/23/610884_200710231701251.jpg" border="0"  alt="" /></a></span></p>  <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（用户可以在网站上发布简历）</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">为了保持一致，贯穿剩下的这本书的例子大多都是为</span><span style="font-size: 12pt; line-height: 150%;">BigMoneyJobs </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">网站而设计的。其他的例子故事可能包括：</span></p> <p style="margin: auto 0cm auto 36pt; text-indent: -18pt;"><span style="font-size: 10pt; font-family: Symbol;">&#183;<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>A user can search for jobs（用户可以查找职位）</p> <p style="margin: auto 0cm auto 36pt; text-indent: -18pt;"><span style="font-size: 10pt; font-family: Symbol;">&#183;<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>A company can post new job openings（公司可以发布新的职位）</p> <p style="margin: auto 0cm auto 36pt; text-indent: -18pt;"><span style="font-size: 10pt; font-family: Symbol;">&#183;<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>A user can limit who can see her resume（用户可以限制那些人可以查看他的简历）</p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%;">&nbsp;</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">因为</span><span style="font-size: 12pt; line-height: 150%;">user stories </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">描述了对客户来说有价值的功能点，所以对这个系统来说下边的例子就不是好的</span><span style="font-size: 12pt; line-height: 150%;">user stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">。</span></p> <p style="margin: auto 0cm auto 36pt; text-indent: -18pt;"><span style="font-size: 10pt; font-family: Symbol;">&#183;<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>The software will be written in C++.（软件应该用C++来编写）</p> <p style="margin: auto 0cm auto 36pt; text-indent: -18pt;"><span style="font-size: 10pt; font-family: Symbol;">&#183;<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>The program will connect to the database through a connection pool（软件应该通过连接池来连接到数据库）.</p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">第一个例子对</span><span style="font-size: 12pt; line-height: 150%;">BigMoneyJobs</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">来说不是个好的</span><span style="font-size: 12pt; line-height: 150%;">user story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">是因为用户根本就不关心使用哪种编程语言。但是，如果这是一个应用程序接口，用户（他本身就是个程序员）写下&#8220;</span><span style="font-size: 12pt; line-height: 150%;">The software will be written in C++</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（软件应该用</span><span style="font-size: 12pt; line-height: 150%;">C++</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">来编写）&#8221;就会很好。</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">第二个</span><span style="font-size: 12pt; line-height: 150%;">story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">在这种情况下也不是个好的</span><span style="font-size: 12pt; line-height: 150%;">user story </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">，因为系统的使用者并不关心应用程序如何连接到数据库的技术细节。</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">也许，你已经读了这些</span><span style="font-size: 12pt; line-height: 150%;">stories </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">并且很惊讶地说，&#8220;等等，使用一个连接池是我这个系统的一个需求&#8221;</span> <span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">如果这样的话，请一定要清楚，编写<span style="font-size: 12pt; line-height: 150%;">stories的</span>关键点在于</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">让客户认可他们的价值，我们将在第二部分&#8220;编写</span><span style="font-size: 12pt; line-height: 150%;">story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">&#8221;里看到一些关于编写</span><span style="font-size: 12pt; line-height: 150%;">Story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">方面的例子。</span></p> <h3><span style="font-size: 14pt; line-height: 173%;">1.1.2 </span><span style="font-size: 14pt; line-height: 173%; font-family: 宋体;">细节在哪呢？</span></h3> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">说</span> <span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">&#8220;</span><span style="font-size: 12pt; line-height: 150%;">A user can&nbsp;search for jobs</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（用户可以查找职位）&#8221;是一件事情，而能否只靠这个作为指导就开始编码和测试却是另外一件事情。因为，细节在哪里呢？类似于下边的这些问题怎么办呢？</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%;">What values can users search on? State? City? Job title? Keywords?</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（用户查询的条件是什么？州？城市？职位？关键字？）</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%;">Does the user have to be a member of the site?</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（用户必须是网站的注册用户吗？）</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%;">Can search parameters be saved?</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（可以保存查询条件么？）</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%;">What information is displayed for matching jobs?</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（查询页面上应该显示哪些信息呢？）</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">许多类似的细节可以当作另外的</span><span style="font-size: 12pt; line-height: 150%;">stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">来描述。实际上，多做几个</span><span style="font-size: 12pt; line-height: 150%;">stories </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">比做一个很大的</span><span style="font-size: 12pt; line-height: 150%;">stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">要好。例如整个的</span><span style="font-size: 12pt; line-height: 150%;">BigMoneyJobs </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">网站可以用这两个</span><span style="font-size: 12pt; line-height: 150%;">stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">来描述：</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>A user can search for a job<span style="font-family: 宋体;">（</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">用户可以找工作）</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>A company can post job openings<span style="font-family: 宋体;">（</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">公司可以发布职位空缺（好机会））</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">很明显，这两个</span><span style="font-size: 12pt; line-height: 150%;">stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">太大了，大到没有太大用处</span><span style="font-size: 12pt; line-height: 150%;">.</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">，在第二章&#8220;编写故事&#8221;中，完整的阐述了故事大小的问题。从一、两个开发人员花费半天或者两个星期来编写和测试一个</span><span style="font-size: 12pt; line-height: 150%;">story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">开始，是一个不错的起点。</span> <span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">公平一些来讲（</span>Liberally interpreted<span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">），上边的两个</span><span style="font-size: 12pt; line-height: 150%;">stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">简单的概括了</span><span style="font-size: 12pt; line-height: 150%;">BigMoneyJobs</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">网站的大部分功能，，每一个大概要花费程序员多于一周的时间。</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">当一个故事太大的时候，他通常会被作为一个</span><span style="font-size: 12pt; line-height: 150%;">Epic</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（译者注：此词本意为史诗级的，我没有找到合适的汉语词汇表达，就是大的故事集的意思）提出</span><span style="font-size: 12pt; line-height: 150%;">.Epics</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">可以被分割成两个或更多个小故事。例如，这个</span><span style="font-size: 12pt; line-height: 150%;">Epic</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">&#8220;</span><span style="font-size: 12pt; line-height: 150%;">A user can search for a job</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（用户可以找工作）&#8221;就可以被分割成这些</span><span style="font-size: 12pt; line-height: 150%;">Stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">。</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%;">A user can search for jobs by attributes like location, salary range, job title, company name, and the date the job was posted.</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（用户可以通过地区</span><span style="font-size: 12pt; line-height: 150%;">, </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">薪水</span><span style="font-size: 12pt; line-height: 150%;">, </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">职位</span><span style="font-size: 12pt; line-height: 150%;">, </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">单位名称</span><span style="font-size: 12pt; line-height: 150%;">, </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">和职位发布日期</span> <span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">来搜索）</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%;">A user can view information about each job that is matched by a search.</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（用户可以查看搜索出来的每个职位的详细信息）</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%;">A user can view detailed information about a company that has posted a job.</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（用户可以查看发布职位空缺信息公司的详细信息）</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">但是，当我们的</span><span style="font-size: 12pt; line-height: 150%;">story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">能够涵盖所有的细节时，我们就不再去分割</span><span style="font-size: 12pt; line-height: 150%;">story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">了。例如，故事&#8220;</span>A user can view information about each job that is matched by a search<span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">&#8221;是非常适度和实用的。我们不需要再去把它进一步的像这样去拆分：</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%;">A user can view a job descrīption.</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（用户可以查看职位描述）</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%;">A user can view a job's salary range.</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（用户可以查看职位薪水）</span></p> <p style="margin: 0cm 0cm 0pt 42.75pt; text-indent: -21pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: Wingdings;">l<span style="font: 7pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-size: 12pt; line-height: 150%;">A user can view the location of a job.</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">（用户可以查看工作地点）</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">总的来说，</span><span style="font-size: 12pt; line-height: 150%;">user story </span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">不需要用专业的需求文档格式夸张的描述成下面这个样子。</span></p> <table style="width: 100%;" width="100%" border="0" cellpadding="0" cellspacing="0"> <thead> <tr> <td style="border: medium none #ece9d8; padding: 3.75pt; background-color: transparent;" valign="top"> <p style="margin: auto 0cm;">4.6)</p></td> <td style="border: medium none #ece9d8; padding: 3.75pt; background-color: transparent;" colspan="2" valign="top"> <p style="margin: auto 0cm;">A user can view information about each job that is matched by a search.</p></td></tr></thead> <tbody> <tr> <td style="border: medium none #ece9d8; padding: 3.75pt; background-color: transparent;" valign="top"> <p>&nbsp;</p></td> <td style="border: medium none #ece9d8; padding: 3.75pt; background-color: transparent;" valign="top"> <p style="margin: auto 0cm;">4.6.1)</p></td> <td style="border: medium none #ece9d8; padding: 3.75pt; background-color: transparent;" valign="top"> <p style="margin: auto 0cm;">A user can view the job descrīption.</p></td></tr> <tr> <td style="border: medium none #ece9d8; padding: 3.75pt; background-color: transparent;" valign="top"> <p>&nbsp;</p></td> <td style="border: medium none #ece9d8; padding: 3.75pt; background-color: transparent;" valign="top"> <p style="margin: auto 0cm;">4.6.2)</p></td> <td style="border: medium none #ece9d8; padding: 3.75pt; background-color: transparent;" valign="top"> <p style="margin: auto 0cm;">A user can view a job's salary range.</p></td></tr> <tr> <td style="border: medium none #ece9d8; padding: 3.75pt; background-color: transparent;" valign="top"> <p>&nbsp;</p></td> <td style="border: medium none #ece9d8; padding: 3.75pt; background-color: transparent;" valign="top"> <p style="margin: auto 0cm;">4.6.3)</p></td> <td style="border: medium none #ece9d8; padding: 3.75pt; background-color: transparent;" valign="top"> <p style="margin: auto 0cm;">A user can view the location of a job.</p></td></tr></tbody></table> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">比坐在这里把这些细节写成</span><span style="font-size: 12pt; line-height: 150%;">stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">更好的方法就是开发团队和客户来一起讨论这些细节。就是说，当这些细节比较重要的时候，就把他拿出来讨论。讨论后，在卡片上添加一些注释是没有错的，就像</span><span style="font-size: 12pt; line-height: 150%;"><a href="mk:@MSITStore:D:%5C%E5%B7%A5%E4%BD%9C%E7%A7%AF%E7%B4%AF%5C%E5%85%B6%E4%BB%96%5C%E7%BF%BB%E8%AF%91%5Cuser.stories.applied.for.agile.software.development.chm::/0321205685/ch01lev1sec2.html#ch01sc02#ch01sc02"><span style="color: windowtext; text-decoration: none;">Story Card 1.2</span></a>.</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">一样。可是，重点是会话，而不是</span><span style="font-size: 12pt; line-height: 150%;">story card</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">上的笔迹。不管是开发人员还是客户都能够在</span><span style="font-size: 12pt; line-height: 150%;">3</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">个月后还指着卡片说&#8220;看，我那时候是这么说的&#8221;。</span><span style="font-size: 12pt; line-height: 150%;">Stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">并不承担法律责任。我们将看到，协议通过可以证明某个</span><span style="font-size: 12pt; line-height: 150%;">story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">被正确实现的测试用例来记录。</span></p> <p><a name="ch01sc02"></a><span style="font-size: 12pt;">Story Card 1.2. A story card with a note.</span></p> <p>&nbsp;</p> <p><span style="font-size: 12pt;"><a href="http://space.flash8.net/space/batch.download.php?aid=70372" target="_blank"><img src="http://space.flash8.net/space/attachments/2007/10/23/610884_200710231701351.jpg" border="0"  alt="" /></a></span></p>  <h3><span style="font-size: 14pt; line-height: 173%;">1.1.3 </span><span style="font-size: 14pt; line-height: 173%; font-family: 宋体;">故事应该有多长呢？</span></h3> <p style="text-indent: 21.75pt; line-height: 150%;"><a name="ch01lev1sec3"></a><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">当我上中学文化课时候，每当我们被指定去写一篇论文，我总是问&#8220;论文必须写多长呢？&#8221;老师是不喜欢这个问题的，但是我仍然认为这个问题是必要的，因为它告诉我老师期望的是什么。这个问题同样也是了解项目用户需求很重要的一点。这些要求最好以可测试的形势被捕获。</span></p> <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">如果你使用的是纸质的笔记卡片，你可以把卡片翻过来，把需求写到背面。这些记录下来的要求提醒怎样测试这个</span><span style="font-size: 12pt; line-height: 150%;">story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">，就像</span><span style="font-size: 12pt; line-height: 150%;"><a href="mk:@MSITStore:D:%5C%E5%B7%A5%E4%BD%9C%E7%A7%AF%E7%B4%AF%5C%E5%85%B6%E4%BB%96%5C%E7%BF%BB%E8%AF%91%5Cuser.stories.applied.for.agile.software.development.chm::/0321205685/ch01lev1sec3.html#ch01sc03#ch01sc03"><span style="color: windowtext; text-decoration: none;">Story Card 1.3</span></a></span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">所显示的那样。如果你使用的是电子系统，它应该有一个地方可以让你加进一些可测试性的提醒。</span></p> <p><a name="ch01sc03"></a><span style="font-size: 12pt;">Story Card 1.3. The back of a story card holds reminders about how to test the story.</span></p> <p>&nbsp;</p> <p><span style="font-size: 12pt;"><a href="http://space.flash8.net/space/batch.download.php?aid=70373" target="_blank"><img src="http://space.flash8.net/space/attachments/2007/10/23/610884_200710231701501.jpg" border="0"  alt="" /></a></span></p>  <p style="text-indent: 21.75pt; line-height: 150%;"><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">测试描述是简短和不完备的，测试用例可以随时添加或者删除。目的是涵盖</span><span style="font-size: 12pt; line-height: 150%;">Story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">的附加信息，以便开发人员知道</span><span style="font-size: 12pt; line-height: 150%;">Story</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">什么时候就算完成了。就像老师的要求对我来说很有用，我可以知道什么时候我写的关于</span><span style="font-size: 12pt; line-height: 150%;">Moby Dick</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">的东西算完成了。它对于开发人员来了解什么时候完成了客户需求一样有用。</span></p> <h3><span style="font-size: 14pt; line-height: 173%;">1.1.4 </span><span style="font-size: 14pt; line-height: 173%; font-family: 宋体;">客户团队</span></h3> <span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">对于一个理想的项目来说，我们会有一个专门的系统最终用户，他为开发人员区分工作的优先级，并回答他们的问题，编写所有的</span><span style="font-size: 12pt; line-height: 150%;">Stories</span><span style="font-size: 12pt; line-height: 150%; font-family: 宋体;">。这个是太理想的情况，所以，我们创建一个客户团队，这个团队里包括那些可以保证软件达到最终用户需求的那些人。这就意味着这个客户团队包括测试人员、管理人员、用户和交互设计人员。</span></div><img src ="http://www.cnitblog.com/stomic/aggbug/74410.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2011-06-17 17:35 <a href="http://www.cnitblog.com/stomic/archive/2011/06/17/74410.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Daily Build--每日构建</title><link>http://www.cnitblog.com/stomic/archive/2011/06/13/74384.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Mon, 13 Jun 2011 10:22:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2011/06/13/74384.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/74384.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2011/06/13/74384.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/74384.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/74384.html</trackback:ping><description><![CDATA[<div><p>在我现在的游戏项目中，基本上每天都要代码，各种游戏资源需要更新。而且每次从SVN服务器上更新代码后都要编译好久。另外资源的更新也是一件很麻 烦的事情，因为我们的所有游戏资源都是统一放在一个FTP上面，每个版本发布之后都会把最新的游戏资源放在里面。每次从FTP上把好几G的数据更新下来很 是费时间。于是我在想能不能写个小程序让这些都自动执行，即能够设定一个时间。例如每天的凌晨从FTP上把资源更新下来，然后再从SVN上把最新的代码更 新下来，最后让程序自动编译。每一步都有详细的log记录，第二天上班后就可以取到最新的版本了。</p>  <p>实际我的上述方法就是Daily Build的雏形。Daily Build又称&#8220;nightly  build&#8221;，是将一个软件项目的所有最新代码取出，从头开始编译、链接，用安装软件包将链接好的软件安装好，运行安装后的软件，运行测试软件包对主要功 能进行测试（smoke test），发现错误并报告错误的完整过程。我的上述想法只是没有自动安装和测试的步骤。</p>  <p>其实实现起来也不是很难，最简单的方法就是写个批处理，然后把批处理程序挂到控制面板的&#8220;Scheduled  Tasks&#8221;中。这种土办法自己要玩玩还行，真要用到项目中去还必须使用专业的软件工具。我在网上搜了一下，发现一款名叫Visual  Build的软件还不错，以后有机会要好好介绍一下。</p>  <p>下面还是稍微介绍下每日构建吧：</p>  <p>在微软软件开发中，每日构建是最重要的过程之一，被称为微软产品开发的&#8220;心跳&#8221;。简单来看，每天构建系统将整个产品解决方案完整构建一遍，生成的目 标文件和安装文件被放置在一个共享位置。接着，安装文件被自动部署到release server上，随后可以自动运行BVT（build  verification test），并将所有结果寄送每个team member的信箱。 </p> <p>微软有一套完善的内部系统来完成整个自动化流程，以及流程管理、reporting等工作，而如果我们没有这套系统，也想实现完整的daily build流程，该怎么做呢？ </p> <p>在VS.NET2003时代，IDE可以控制整个方案的构建，但是所有的构建逻辑被IDE控制，对于开发人员来说，整个构建流程就像一个黑箱，很难 修改和管理。当然可以使用PreBuildEvent和PostBuildEvent来控制，但是这些event都写在csproj/vbproj文件 中，不便于修改，不适于扩展。而且使用IDE来做每日构建的话，要求构建系统本身装有VS.NET，这会带来额外的成本。另一种办法是使用NAnt，通过 XML配置文件，来管理构建流程，这会使得整个流程易于修改，可扩展，并且不要求构建系统安装IDE，只需要有framework即可。问题是使用 NAnt必须额外写一堆复杂的XML配置文件，足以让很多developer看了头疼。 </p> <p>VS.NET2005中引入了一套全新的构建系统：MSBuild。简单来讲，MSBuild可以直接读取csproj文件，控制csc/vbc等 编译器，生成整个方案。实际上，VS2005的IDE自身就是调用MSBuild来完成编译的，这与VS2003有很大的不同。并且由于VS2005的 csproj文件服从MSBuild的配置schema，因此我们可以直接使用csproj，稍稍修改一下，就能组织起完整的构建流程了。 </p>  <p>虽然这是一个非常简单的过程，但却有非常重要的意义： </p> <p><br />1、能最小化集成风险 </p> <p>项目组可能遇到的一个很大的风险是，项目组成员根据不同的系统功能各自开发不同的代码，但是当这些代码集成为一个系统的时候，也许系统完成不了预期 的功能。这种风险的发生取决于项目中的这种不兼容性多久才被发现，由于程序界面已经发生了变化，或者系统的主要部分已经被重新设计和重新实现了，相应的排 错工作将非常困难和耗时。极端情况下，集成的错误可能回导致项目被取消掉。每日构造和冒烟测试可以使这种集成错误变得非常小，而且便于解决，防止了很多集 成问题的产生。 </p>  <p>2、能减小产品低质量的风险 </p> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这种风险是和集成不成功、集成出错相关联的。每天对集成的代码做一些少量的冒烟测试，即可杜绝项目中那些基本的质量问题。通过这种方式，使系统达到一种周知的良好状态，维护这样的系统可以防止系统逐步恶化到耗费大量时间排查质量问题的地步。 </p>  <p>3、能简单化错误诊断 </p> <p>当系统每天都进行build和测试时，系统任何一天发生的错误都能够变得十分精细，便于排查。比如在17日系统还运行正常，18日就出错了，那么只需要检查这两次build之间的代码变化就可以了。 </p> <p>&nbsp; </p> <p>4、能极大鼓舞项目组的士气 </p> <p>看到产品的不断成长，能够极大的鼓舞项目组的士气，有时甚至不管这个产品到底用来做什么。开发人员可能会为系统显示了一个矩形而感到激动。通过每日构造，产品每天进步一点点，保证项目士气的持续高涨。 </p> <p>&nbsp; </p> <p>进行每日构造和冒烟测试 </p> <p>虽然说这是一个简单枯燥的活，每天进行build，每天进行测试，但也有着一些值得注意的细节： </p> <p>&nbsp; </p> <p>1、每天坚持 </p> <p>每日构造，最重要的就是&#8220;每日&#8221;。如Jim  McCarthy所说，把每日构造看作是项目的&#8220;心跳&#8221;，没有&#8220;心跳&#8221;的话，项目也就死了(Dynamics of Software  Development, Microsoft Press, 1995)。Michael Cusumano and Richard W.  Selby描述了另外一种隐含的比喻，把每日构造比作项目的&#8220;同步脉冲&#8221;(Microsoft Secrets, The Free Press,  1995)。  不同开发人员写的代码在他们的&#8220;脉冲&#8221;之间肯定都会存在&#8220;同步&#8221;的差异，但是必须有这样一个&#8220;同步脉冲&#8221;，使得这些代码能够组合为一个整体。当项目组坚持 每天把这些不同的&#8220;脉冲&#8221;组合到一起的时候，开发人员脱离整体的情况就会得到极大程度的杜绝。 </p> <p>&nbsp; </p> <p>有些项目组把这一过程简化为&#8220;每周build一次&#8221;。这样带来的问题是，某一次build失败后，可能要回溯好几周才能找到原因。如果这种情况发生的话，已经得不到经常build带来的好处了。 </p> <p>&nbsp; </p> <p>2、严格检查每一次build </p> <p>要保证每一次build的成功，就必须保证build后的结果（也可称为build）是可以正常运行的，如果build不可运行，那么本次build被认为是不成功的，同时应该将修复此次build的工作提高到项目组最高级别来处理。 </p> <p>对于如何衡量一个build，每一个项目组都会定义一些自己的标准，这些标准需要设定一个严格的质量级别来处理那些特别严重的缺陷，同时也需要具有一定的伸缩性来忽略掉那些微不足道的缺陷，一些不适当的关心也许会使整个过程举步为艰。 </p> <p>一个好的build起码应该具备以下条件： </p> <p>&#9679;能够成功编译所有的文件、库，以及其它相关组件； </p> <p>&#9679;能够成功链接所有的文件、库，以及其它相关组件； </p> <p>&#9679;不能存在任何使得系统无法运行或者运行出错的高级别故障； </p> <p>&#9679;当然，必须通过冒烟测试 </p> <p>&nbsp; </p> <p>3、每天进行冒烟测试 </p> <p>冒烟测试应该是对整个系统流程从输入到输出的完整测试。测试不必是面面俱到的，但是应该能够发现系统中较大的问题。冒烟测试应该是足够充分的，通过了冒烟测试的build就可以认为是经过充分测试、足够稳定的。 </p> <p>不进行冒烟测试的build是没有太大价值的。冒烟测试就像一个哨兵，在阻止着产品质量恶化和集成问题的产生，不进行冒烟测试，每日构造可能会变成浪费时间的练习。 </p> <p>冒烟测试必须随着系统的扩充而扩充。最初，冒烟测试可能是非常简单的，比如验证系统是否会打印&#8220;Hello World&#8221;，随着系统功能的扩充，冒烟测试需要越来越充分。最初的冒烟测试也许只需要几秒钟来执行，逐渐地，测试可能会花费30分钟，1小时，甚至更长。 </p> <p>&nbsp; </p> <p>4、建立一个专门的build小组 </p> <p>&nbsp;&nbsp;&nbsp;  在很多项目组，维护每日构造，并更新冒烟测试用例，会耗费一个人工作的大部分时间。因此在一些大的项目中，这项工作独立成不止一个人来完成的全职工作。比 如在 Windows NT 3.0的研发中，就有一个由四个全职人员组成的专门的build小组(Pascal Zachary,  Showstopper!, The Free Press, 1994)。 </p> <p>&nbsp; </p> <p>5、为build增加修订，如果这样做有意义的话 </p> <p>&nbsp;&nbsp;&nbsp; 一般开发人员不会每天都经常向系统中快速的增加实际的代码，通常是每隔几天，他们在开发好完成某个功能的一套代码以后，然后集成到整个系统中。 </p> <p>&nbsp; </p> <p>6、规定一些导致build失败的惩罚措施 </p> <p>很多执行每日构造的项目组都会规定一些惩罚措施，来惩罚那些导致build失败的行为。从最开始，项目组成员就清楚的知道，build的正常执行是 项目组的头等大事。一个失败的build是项目组的意外，无法成为项目组工作的准则。必须坚持：导致build失败的同事，必须停下手中的工作，首先来解 决build失败的问题。如果一个项目组的build经常失败的话，久而久之的，再来谈build的正确性就没有意义了。 </p> <p>有种轻松的惩罚措施，能够突出解决问题的优先性。Some groups give out lollipops to each  "sucker" who breaks the build. This developer then has to tape the  sucker to his office door until he fixes the problem.  有些项目组会惩罚犯错的同事戴上山羊角，或者向一个项目基金捐献5块钱。 </p> <p>有些项目组对此的惩罚就有点残酷了。微软的开发人员，在一些知名度很高、很重要的产品如Windows NT，Windows  95，Excel等产品后期研发中，被要求随时带着寻呼机，如果你的代码导致build失败的话，即使是凌晨3点钟，也会要求你立即来处理这个问题。 </p> <p>&nbsp; </p> <p>7、即使在压力下也需坚持每日构造和冒烟测试 </p> <p>当项目进度的压力越来越大时，维护每日构造的工作看起来有些浪费时间，但是恰恰相反。在压力之下，开发人员丢掉一些平时的规定，会采用一些设计和实 现的捷径，这在平时压力较小的环境下一般时不会用的。代码的review和单元测试也可能会比平时粗心一些，这些代码的状态变化也会比平时快很多。 </p> <p>为防止这种情况的出现，每日构造会坚持相关的规定，让压力下的项目保持在正轨上。代码仍然每天在不断变化，但是构造过程使得这种变化每天都可控。 </p> <p>&nbsp; </p> <p>谁能够从每日构造这种过程中得到好处呢？一些开发人员会抗议说，由于他们的项目太大，每天进行build是没有实际意义的。但是为什么现在最复杂的 软件项目组却能够成功的执行每日构造的制度呢？本文首发时，Windows  NT包括了560万行代码、分布在4万个源程序文件中，项目组仍然可以坚持每日构造。 </p></div><img src ="http://www.cnitblog.com/stomic/aggbug/74384.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2011-06-13 18:22 <a href="http://www.cnitblog.com/stomic/archive/2011/06/13/74384.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Web Testing Checklist </title><link>http://www.cnitblog.com/stomic/archive/2009/10/28/62172.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Tue, 27 Oct 2009 17:44:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2009/10/28/62172.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/62172.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2009/10/28/62172.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/62172.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/62172.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: Usability&nbsp;                                    Items                                    Questions                                    Answers                          ...&nbsp;&nbsp;<a href='http://www.cnitblog.com/stomic/archive/2009/10/28/62172.html'>阅读全文</a><img src ="http://www.cnitblog.com/stomic/aggbug/62172.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2009-10-28 01:44 <a href="http://www.cnitblog.com/stomic/archive/2009/10/28/62172.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Generic Installation Testing CheckList</title><link>http://www.cnitblog.com/stomic/archive/2009/10/28/62171.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Tue, 27 Oct 2009 17:43:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2009/10/28/62171.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/62171.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2009/10/28/62171.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/62171.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/62171.html</trackback:ping><description><![CDATA[<h1 class=postTitle><a class=postTitle2 id=ctl04_TitleUrl href="http://www.cnblogs.com/mayingbao/archive/2006/08/02/466114.html"><font color=#075db3>Generic Installation Testing CheckList</font></a> </h1>
<div class=clear></div>
<div class=postBody><strong><u><font face=Century size=2><span>Generic Installation Testing <span>CheckList</span></span></font></u></strong><br><br>
<p><font face=Century size=2><span><span>1.<font face="Times New Roman" size=1><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Support of the different platforms and configurations needed? </span></font></p>
<p><font face=Century size=2><span><span>2.<font face="Times New Roman" size=1><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Does Installation in &#8220;clean state&#8221; working? </span></font></p>
<p><font face=Century size=2><span><span>3.<font face="Times New Roman" size=1><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Does the installer is able to calculate needed disk space? </span></font></p>
<p><font face=Century size=2><span><span>4.<font face="Times New Roman" size=1><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span></span></font><font face=Century color=black size=2><span>Does the installer capturing the baseline free space before launching the installer?</span></font><font face=Century size=2><span> </span></font></p>
<p><font face=Century size=2><span><span>5.<font face="Times New Roman" size=1><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span></span></font><font face=Century color=black size=2><span>Does the amount of space the installer claims it needs for the various type of installation is actually taken up or is there any discrepancy?</span></font><font face=Century size=2><span> </span></font></p>
<p><font face=Century size=2><span><span>6.<font face="Times New Roman" size=1><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span></span></font><font face=Century color=black size=2><span>How much disk space is used by the installer if the installation is quit midway (this would help gathering information on temp files that&#8217;s deleted on quitting).</span></font><font face=Century size=2><span> </span></font></p>
<p><font face=Century size=2><span><span>7.<font face="Times New Roman" size=1><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Does the installation recover in case an error is met during the installation? </span></font></p>
<p><font face=Century size=2><span><span>8.<font face="Times New Roman" size=1><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Does the installer able to Repair any corrupt installation?</span></font></p>
<p><font face=Century size=2><span><span>9.<font face="Times New Roman" size=1><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></font></span></span></font><font face=Century color=black size=2><span>If the application installed properly fro each type of installation (for typical, custom and complete)? </span></font><font face=Century size=2></font></p>
<p><font face=Century color=black size=2><span><span>10.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century color=black size=2><span>Does Installation over network working? </span></font></p>
<p><font face=Century color=black size=2><span><span>11.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century color=black size=2><span>If any file association is made during installation, upon uninstallation, does the association is removed and the base file association is returned to the files? </span></font></p>
<p><font face=Century size=2><span><span>12.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Does running the installer, followed by launching the program to run some tests, and then running the uninstaller, also return your machine to the base state? </span></font></p>
<p><font face=Century size=2><span><span>13.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Does uninstallation leave any registry entry, data files in the system? </span></font></p>
<p><font face=Century size=2><span><span>14.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>If there <span>exists</span> a version of the app to be installed already on the machine, does the installer identify that? </span></font></p>
<p><font face=Century size=2><span><span>15.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Does the installer identify if some needed components (such as, MSDE etc.) are already installed on the system? </span></font></p>
<p><font face=Century size=2><span><span>16.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Does running two instances of the installer should prompt a message to the user that an installation setup is already running? </span></font></p>
<p><font face=Century size=2><span><span>17.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>If the&nbsp;user logged in, doesn&#8217;t have write permission for the machine, how installation reacts to this? &nbsp;</span></font></p>
<p><font face=Century size=2><span><span>18.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>What happens if the installer tries to install to a directory where there is no write access?</span></font></p>
<p><font face=Century size=2><span><span>19.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><tt><font face=Century size=2><span>Is the installation path configurable/non configurable?</span></font></tt><font face=Century size=2></font></p>
<p><font face=Century size=2><span><span>20.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Check to ensure that when installing the product, it should provide a browse button which enables the user to install at any folder, and it should provide by default folder (For ex: C:\program files)</span></font></p>
<p><font face=Century size=2><span><span>21.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Is the registering and un-registering the components (<span>dlls</span>) on installation and uninstallation is occurring properly?</span></font></p>
<p><font face=Century size=2><span><span>22.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Are all the files installed in the respective folders and path?</span></font></p>
<p><tt><font face=Century size=2><span><span>23.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font></tt><tt><font face=Century size=2><span>Whether all the files/registry values/services are installed</span></font></tt><font face=Century size=2><span> <tt><font face=Century><span>properly. </span></font></tt></span></font></p>
<p><font face="Times New Roman" size=3><span><span>24.<font face="Times New Roman" size=1>&nbsp;</font></span></span></font><tt><font face=Century size=2><span>Check whether the shortcuts are installed properly and also the</span></font></tt><font face=Century size=2><span> <tt><font face=Century><span>PATH (any other ENV variables) is updated properly. (It can be CURRENT</span></font></tt> <tt><font face=Century><span>USERS profile or All Users profile based on your requirements).</span></font></tt></span></font></p>
<p><font face=Century size=2><span><span>25.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Does the installation support, '<span>UnInstall</span>', 'Modify', '<span>ReInstall</span>' options?? If yes,&nbsp;does it&nbsp;work? </span></font></p>
<p><font face=Century size=2><span><span>26.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><tt><font face=Century size=2><span>If the installer is supporting upgrade feature, does it preserve all the</span></font></tt><font face=Century size=2><span> <tt><font face=Century><span>necessary settings (mostly user preferences)?</span></font></tt></span></font></p>
<p><font face=Century size=2><span><span>27.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><tt><font face=Century size=2><span>Check for the user privileges before starting installation. (In</span></font></tt><font face=Century size=2><span> <tt><font face=Century><span>most of the cases, installer requires ADMIN privileges).</span></font></tt></span></font></p>
<p><tt><font face=Century size=2><span><span>28.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font></tt><tt><font face=Century size=2><span>Check the uninstaller entry in add-remove programs. (Check for</span></font></tt><font face=Century size=2><span> <tt><font face=Century><span>display string, Display icon and Support information etc.).</span></font></tt></span></font></p>
<p><font face="Times New Roman" size=3><span><span>29.<font face="Times New Roman" size=1>&nbsp;</font></span></span></font><font face=Century size=2><span>Does running the installer, and then running the uninstaller, return the machine to the base state? </span></font></p>
<p><tt><font face=Century size=2><span><span>30.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font></tt><tt><font face=Century size=2><span>Reinstallation should, apart from identifying previous</span></font></tt><font face=Century size=2><span> <tt><font face=Century><span>versions, also should give an option to Remove and</span></font></tt> <tt><font face=Century><span>Repair.</span></font></tt></span></font></p>
<p><font face="Times New Roman" size=3><span><span>31.<font face="Times New Roman" size=1>&nbsp;</font></span></span></font><font face=Century size=2><span>Check to ensure that license key is properly stored in Windows Registry library.</span></font></p>
<p><font face=Century size=2><span><span>32.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Check to ensure that if&nbsp;an evaluation version is installed, then a proper message should be displayed when the date of period is expired for evaluation version with proper error message.</span></font></p>
<p><font face=Century size=2><span><span>33.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Check to ensure that, if&nbsp;Windows Services are installed then it should install in the Services folder of windows directory.</span></font></p>
<p><font face=Century size=2><span><span>34.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>Check to ensure that if any product is installed and it is dependent on some other product, then it should give proper message as "The Product is not installed and it should exit". Check for dependencies.</span></font></p>
<p><font face=Century size=2><span><span>35.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>If the product to be installed uses any third party dll and if it is already installed by some other product&nbsp;confirm that the current installation doesn&#8217;t un-register/tamper it and uses the&nbsp;existing&nbsp;one.</span></font></p>
<p><font face=Century size=2><span><span>36.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>If the dll is already there in the system how does the installation work?</span></font></p>
<p><font face=Century size=2><span><span>37.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>And also while uninstalling, check should be made as to whether that the shared dll is left without affecting other product.</span></font></p>
<p><font face=Century size=2><span><span>38.<font face="Times New Roman" size=1><span>&nbsp;&nbsp; </span></font></span></span></font><font face=Century size=2><span>&#8220;Usability&#8221; consideration of the installer.</span></font></p>
</div>
<img src ="http://www.cnitblog.com/stomic/aggbug/62171.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2009-10-28 01:43 <a href="http://www.cnitblog.com/stomic/archive/2009/10/28/62171.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>GUI CheckList</title><link>http://www.cnitblog.com/stomic/archive/2009/10/28/62169.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Tue, 27 Oct 2009 17:42:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2009/10/28/62169.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/62169.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2009/10/28/62169.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/62169.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/62169.html</trackback:ping><description><![CDATA[<p><span>表单</span><span>Check List</span></p>
<p>&nbsp;</p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>在数字框中是否能输入文本？</span><span> </span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>是否允许通配符？</span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>是否可以为空？</span> </p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>输入长度限制是否合理</span><span>?</span></p>
<p><span><span>&gt;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>&nbsp;</span><span>例如：身份证框限制为</span><span>15</span><span>字符，不合理</span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>复选框和单选按钮的默认值是否正确</span><span>? </span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>是否只有一个单选框能被选中</span><span>? </span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>复选框是否出发了预期的事件</span></p>
<p>&nbsp;</p>
<p><span>页面</span><span> Check List</span></p>
<p>&nbsp;</p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>当页面出现较长列表时，是否有滚动条？</span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>调整控件或框架的大小，是否对页面造成非预期的影响？</span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>当用户选中了页面中的一个复选框，之后回退一个页面，再前进一个页面，复选框是否还处于选中状态？</span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>如果浏览器调整大小，页面是否还能完全显示？</span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>刷新后界面是否正常显示？</span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>控件在特定情况下是否被设置成</span><span>disable</span><span>状态？</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>导航</span><span> Check List</span></p>
<p>&nbsp;</p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>导航是否直观</span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Web</span><span>系统的主要部分是否可通过主页存取</span></p>
<p><span><span>&#8211;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><span>Web</span><span>系统是否有站点地图、搜索引擎或其它的导航帮助</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span>对于屏幕显示来说包括：</span><span><br></span><span>检查显示的布局；</span><span><br></span><span>检查域和按钮的顺序；</span><span><br></span><span>检查域的尺寸；</span><span><br></span><span>检查字体的大小和风格；</span><span><br></span><span>检查文本的含义；</span><span><br></span><span>检查拼写错误；</span><span><br></span><span>检查屏蔽域；</span><span><br></span><span>检查只读域；</span><span><br></span><span>检查图片；</span><span><br></span><span>检查按钮的状态；</span><span><br></span><span>检查按钮的尺寸；</span><span><br></span><span>检查按钮的图标和名字；</span><span><br></span><span>检查是否有重复的图标；</span><span><br></span><span>检查指针是否在第一个可输入域；</span><span><br></span><span>检查</span><span>TAB</span><span>键的次序；</span><span><br><br></span><strong><span>对于域来说包括：</span></strong><span><br></span><span>检查可编辑性；</span><span><br></span><span>检查域间的移动；</span><span><br></span><span>检查分界条件；</span><span><br></span><span>检查有效分界符；</span><span><br></span><span>检查无效分界符；</span><span><br></span><span>检查连续多个有效分界符；</span><span><br></span><span>检查仅一个分界符输入；</span><span><br></span><span>检查多余空格的截取；</span><span><br></span><span>检查只读域和屏蔽域在</span><span>TAB</span><span>时的状态；</span><span><br><br></span><strong><span>对于数字域来说包括：</span></strong><strong><span><br></span></strong><span>检查正数值；</span><span><br></span><span>检查负数值；</span><span><br></span><span>检查零值；</span><span><br></span><span>检查小数点；</span><span><br></span><span>检查特殊字符加数字；</span><span><br></span><span>检查字母加数字；</span><span><br></span><span>检查</span><span>ASCII</span><span>值；</span><span><br></span><span>检查重复值；</span><span><br></span><span>检查空值；</span><span><br><br><br></span><span>对于字符域来说包括：</span><span><br></span><span>检查仅有字母；</span><span><br></span><span>检查仅有数字；</span><span><br></span><span>检查字母数字；</span><span><br></span><span>检查允许的特殊字符；</span><span><br></span><span>检查禁止的特殊字符；</span><span><br></span><span>检查包含特殊字符的字母数字；</span><span><br></span><span>检查</span><span>ASCII</span><span>值；</span><span><br><br></span><span>对于字母域来说包括：</span><span><br></span><span>检查字母；</span><span><br></span><span>检查数字值；</span><span><br></span><span>检查字母数字值；</span><span><br></span><span>检查特殊字符；</span><span><br></span><span>检查</span><span>ASCII</span><span>值；</span><span><br><br></span><span>对于时间域来说包括：</span><span><br></span><span>检查字符</span><span>?</span><span>和</span><span>/</span><span>；</span><span><br></span><span>检查其它特殊字符；</span><span><br></span><span>检查字母数字值；</span><span><br></span><span>检查正确的格式；</span><span><br></span><span>检查错误的格式；</span><span><br></span><span>检查错误的日期数字；</span><span><br></span><span>检查正确的日期数字；</span><span><br></span><span>检查日历表；</span><span><br><br></span><span>对于错误信息和警告信息来说：</span><span><br></span><span>检查错误信息和警告信息的含义；</span><span><br></span><span>检查错误信息和警告信息的一致性；</span><span><br></span><span>检查确定位置的错误信息；</span><span><br></span><span>检查错误信息后的光标位置；</span><span><br></span><span>检查所有异常对应的错误信息；</span><span><br></span><span>检查错误信息的格式；</span><span><br><br></span><span>对于普通的检查来说：</span><span><br></span><span>检查文本域和字符域输入是否左对齐；</span><span><br></span><span>检查数字域输入是否右对齐；</span><span><br></span><span>检查标签的切换；</span><span><br></span><span>检查重复的名字；</span><span><br></span><span>检查可删除的表格；</span><span><br></span><span>检查表格的多选；</span><span><br></span><span>检查过滤器的逻辑性；</span><span><br></span><span>检查多个过滤器的逻辑性；</span><span><br></span><span>检查重复的序列号；</span><span><br></span><span>检查显示切换；</span><span><br></span><span>检查快捷键；</span><span><br></span><span>检查工具栏提示；</span><span><br></span><span>检查日期域是否居中；</span><span><br></span><span>检查选择项的高显；</span><span><br></span><span>检查选择符；</span><span><br></span><span>检查显示窗口的风格统一性；</span><span><br><br><br></span><span>对于按键的功能包括：</span><span><br>New button</span><span>：</span><span><br></span><span>检查包含</span><span>next</span><span>和</span><span>cancel</span><span>按键的子窗口的显示；</span><span><br></span><span>检查子窗口显示的内容；</span><span><br>Add button</span><span>：</span><span><br></span><span>检查包含</span><span>save</span><span>和</span><span>cancel</span><span>按键的子窗口的显示；</span><span><br>Edit button</span><span>：</span><span><br></span><span>检查在未选择项目情况下点击后的警告信息；</span><span><br></span><span>检查包</span><span>update</span><span>和</span><span>cancel</span><span>按键的子窗口的显示；</span><span><br></span><span>检查选择的项目是否显示在制定的位置；</span><span><br>Copy button</span><span>：</span><span><br></span><span>检查在未选择项目情况下点击后的警告信息；</span><span><br></span><span>检查点击后的确认信息；</span><span><br></span><span>检查插入后的复制数据；</span><span><br>Delete button</span><span>：</span><span><br></span><span>检查在未选择项目情况下点击后的警告信息；</span><span><br></span><span>检查点击后的确认信息；</span><span><br></span><span>检查删除后的数据；</span><span><br>Run button</span><span>：</span><span><br></span><span>检测运行时的参数窗口；</span><span><br></span><span>检查执行结果；</span><span><br></span><span>检查未选择项目情况下点击后的警告信息；</span><span><br>Back button</span><span>：</span><span><br></span><span>检查是否回到上一屏幕；</span><span><br>Next button</span><span>：</span><span><br></span><span>检查是否显示下一屏幕；</span><span><br>Finish button</span><span>：</span><span><br></span><span>检查数据是否进入数据库；</span><span><br></span><span>检查完成屏幕的显示；</span><span><br>Cancel button</span><span>：</span><span><br></span><span>检查确认信息；</span><span><br></span><span>检查是否有其它键执行同样功能；</span><span><br></span><span>检测是否能能够正确处理；</span></p>
<img src ="http://www.cnitblog.com/stomic/aggbug/62169.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2009-10-28 01:42 <a href="http://www.cnitblog.com/stomic/archive/2009/10/28/62169.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>界面测试CheckList </title><link>http://www.cnitblog.com/stomic/archive/2009/10/28/62170.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Tue, 27 Oct 2009 17:42:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2009/10/28/62170.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/62170.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2009/10/28/62170.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/62170.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/62170.html</trackback:ping><description><![CDATA[<h1 class=postTitle><a class=postTitle2 id=ctl04_TitleUrl href="http://www.cnblogs.com/mayingbao/archive/2007/01/16/621885.html"><font color=#075db3>界面测试CheckList</font></a> </h1>
<div class=clear></div>
<div class=postBody>界面测试CheckList<br><br>&nbsp;
<p align=left><span>目前流行的界面风格有三种方式：多窗体、单窗体以及资源管理器风格，无论那种风格，以下规则是应该被重视的。</span><span><br></span><strong><span>1</span></strong><strong><span>：易用性</span></strong><span>：</span><span><br></span><span>　<span>　</span></span><span>按钮名称应该易懂，用词准确，屏弃没楞两可的字眼，要与同一界面上的其它按钮易于区分，能望文知意最好</span><span>。理想的情况是用户不用查阅帮助就能知道该界面的功能并进行相关的正确操作。</span><span><br></span><span>易用性细则：<span><br>1):</span><span>完成相同或相近功能的按钮用<span>Frame</span>框起来，常用按钮要支持快捷方式。</span><span><br>2):</span><span>完成同一功能或任务的元素放在集中位置，减少鼠标移动的距离。</span><span><br>3):</span><span>按功能将界面划分区域块，用<span>Frame</span>框括起来<span>,</span>并要有功能说明或标题。<span><br></span></span><span>4):</span><span>界面要支持键盘自动浏览按钮功能，即按<span>Tab</span>键、回车键的自动切换功能。<span><br></span></span><span>5):</span><span>界面上首先要输入的和重要信息的控件在<span>Tab</span>顺序中应当靠前<span>,</span>位置也应放在窗口上较醒目的位置。 <span><br></span></span><span>6)<span>:</span></span><span>同一界面上的控件数最好不要超过<span>10</span>个，多于<span>10</span>个时可以考虑使用分页界面显示</span>。<span><br>7):</span>分页界面要支持在页面间的快捷切换，常用组合快捷键<span>Ctrl+Tab<br>8):</span><span>默认按钮要支持<span>Enter</span>及选操作，即按<span>Enter</span>后自动执行默认按钮对应操作。</span></span></p>
<p align=left><span>9):</span><span>可写控件检测到非法输入后应给出说明并能自动获得焦点。<span><br></span></span><span>10):Tab</span><span>键的顺序与控件排列顺序要一致，目前流行总体从上到下，同时行间从左到右的方式。<span><br>11):</span>复选框和选项框按<span>选择几率的高底而先后排列。</span><span><br>12):</span><span>复选框和选项框要有默认选项，并支持<span>Tab</span>选择。<span><br></span></span><span>13):</span><span>选项数相同时多用选项框而不用下拉列表框<span>//</span>两个的</span><span><br>14):</span><span>界面空间较小时使用下拉框而不用选项框。<span><br></span></span><span>15):</span><span>选项数较少时使用选项框，相反使用下拉列表框。<span><br></span></span><span>16):</span>专业性强的软件要使用相关的专业术语，通用性界面则提倡使用通用性词语。</span><span><br></span><strong><span>2</span></strong><strong><span>：</span></strong><strong><span> </span></strong><strong><span>规范性：</span></strong><span><br></span><span>通常界面设计都按<span>Windows</span>界面的规范来设计，可以说：界面遵循规范化的程度越高，则易用性相应的就越好。小型软件一般不提供工具厢。 <span><br></span>规范性细则：<span><br>1):</span>常用菜单要有命令快捷方式。<span><br>2):</span><span>完成相同或相近功能的菜单用横线隔开放在同一位置。<span><br></span></span><span>3):</span>菜单前的图标能直观的代表要完成的操作。<span><br>4):</span>菜单深度一般要求最多控制在三层以内。<span><br>5):</span><span>工具栏要求可以根据用户的要求自己选择定制。<span><br></span></span><span>6):</span>相同或相近功能的工具栏放在一起。<span><br>7):</span><span>工具栏中的每一个按钮要有及时提示信息。<span><br></span></span><span>8):</span><span>一条工具栏的长度最长不能超出屏幕宽度。<span><br></span></span><span>9): </span>工具栏的图标能直观的代表要完成的操作。<span><br>10):</span><span>系统常用的工具栏设置默认放置位置。<span><br></span></span><span>11):</span>工具栏太多时可以考虑使用工具箱。<span><br>12):</span>工具箱要具有可增减性，由用户自己根据需求定制。<span><br>13):</span><span>工具箱的默认总宽度不要超过屏幕宽度的<span>1/5</span>。<span><br></span></span><span>14): </span>状态条要能显示用户切实需要的信息，常用的有：<span><br></span>目前的操作、系统状态、用户位置、用户信息、提示信息、错误信息等，<span>如果某一操作需要的时间较长，还应该显示进度条和进程提示。<span><br></span></span><span>15)</span>：<span>滚动条的长度要根据显示信息的长度或宽度能及时变换，以利于用户了解显示信息的位置和百分比。<span><br></span></span><span>16)</span>：状态条的高度以放置五好字为宜，滚动条的宽度比状态条的略窄。<span><br>17)</span>：<span>菜单和工具条要有清楚的界限<span>;</span>菜单要求凸出显示，这样在移走工具条时仍有立体感。<span><br></span></span><span>18)</span>：菜单和状态条中通常使用<span>5</span>号字体。工具条一般比菜单要宽，但不要宽的太多，否则看起来很不协调。<span><br>19): </span>右键快捷菜单采用与菜单相同的准则。</span><span><br></span><strong><span>3</span></strong><strong><span>：帮助设施：</span></strong><span><br></span><span>系统应该提供详尽而可靠的帮助文档，在用户使用产生迷惑时可以自己寻求解决方法。</span><span><br></span><span>帮助设施细则：<span><br>1)</span>：<span>帮助文档中的性能介绍与说明要与系统性能配套一致</span>。<span>(</span>我们的系统帮助文档都是系统的组先时期的说明，让人困惑<span>)</span>。<span><br>2)</span>：<span>打包新系统时，对作了修改的地方在帮助文档中要做相应的修改。<span><br></span></span><span>3)</span>：操作时要提供及时调用系统帮助的功能。常用<span>F1</span>。<span><br>4)</span>：<span>在界面上调用帮助时应该能够及时定位到与该操作相对的帮助位置</span>。也就是说帮助要有即时针对性。<span><br>5)</span>：最好提供目前流行的联机帮助格式或<span>HTML</span>帮助格式。<span><br>6)</span>：<span>用户可以用关键词在帮助索引中搜索所要的帮助，当然也应该提供帮助主题词。<span><br></span></span><span>7)</span>：如果没有提供书面的帮助文档的话，最好有打印帮助的功能。<span><br>8)</span>：<span>在帮助中应该提供我们的技术支持方式，一旦</span>用户难以自己解决可以方便的寻求新的帮助方式。</span></p>
<p align=left><span>9):</span><span>在帮助中应提供留言管理功能，让用户可以方便进行沟通。</span></p>
<p align=left><strong><span>4</span></strong><strong><span>：合理性：</span></strong><span><br></span><span>屏幕对角线相交的位置是用户直视的地方，正上方四分之一处为易吸引用户注意力的位置，在放置窗体时要注意利用这两个位置。</span><span><br></span><span>合理性细则：<span><br>1)</span>：<span>父窗体或主窗体的中心位置应该在对角线焦点附近。<span><br></span></span><span>2)</span>：子窗体位置应该在主窗体的左上角或正中。<span><br>3)</span>：多个子窗体弹出时应该依次向右下方偏移，以显示窗体出标题为宜。<span><br>4)</span>：重要的命令按钮与使用较频繁的按钮要放在界面上注目的位置。<span><br>5)</span>：<span>错误使用容易引起界面退出或关闭的按钮不应该放在易点击的位置。横排开头或最后与竖排最后为易点位置。<span><br></span></span><span>6)</span>：<span>与正在进行的操作无关的按钮应该加以屏蔽<span>(Windows</span>中用灰色显示，没法使用该按钮<span>)</span>。<span><br></span></span><span>7)</span>：<span>对可能造成数据无法恢复的操作必须提供确认信息<span>,</span>给用户放弃选择的机会。<span><br></span></span><span>8)</span>：非法的输入或操作应有足够的提示说明。<span><br>9):<span> </span></span><span>对运行过程中出现问题而引起错误的地方要有提示，让用户明白错误出处，避免形成无限期的等待。<span><br></span></span><span>10): </span><span>提示、警告、或错误说明应该清楚、明了、恰当。</span></span><span><br></span><strong><span>5</span></strong><strong><span>：美观与协调性：</span></strong><span><br></span><span>界面应该大小适合美学观点，感觉协调舒适，能在有效的范围内吸引用户的注意力。</span><span><br></span><span>美观与协调性细则：<span><br>1): </span><span>长宽接近黄金点比例，切忌长宽比例失调、或宽度超过长度。<span><br></span></span><span>2): </span>布局要合理<span>,</span>不宜过于密集，也不能过于空旷，合理的利用空间。<span><br>3): </span>按钮大小基本相近，忌用太长的名称，免得占用过多的界面位置。<span><br>4): </span>按钮的大小要与界面的大小和空间要协调。<span><br>5): </span>避免空旷的界面上放置很大的按钮。<span><br>6)</span>：放置完控件后界面不应有很大的空缺位置。<span><br>7): </span>字体的大小要与界面的大小比例协调<span>, </span>通常使用的字体中宋体<span>9-12</span>较为美观，很少使用超过<span>12</span>号的字体。<span><br>8): </span>前景与背景色搭配合理协调，反差不宜太大，最好少用深色，如大红、大绿等。常用色考虑使用<span>Windows</span>界面色调。<span><br>9): </span>如果使用其它颜色，主色调要柔和，具有亲和力与磁力，坚决杜绝刺目的颜色。<span><br>10): </span>大型系统常用的主色有<span>"#E1E1E1"</span>、<span>"#EFEFEF"</span>、<span>"#C<st1:chmetcnv TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="0" UnitName="C" w:st="on">0C</st1:chmetcnv><st1:chmetcnv TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="0" UnitName="C" w:st="on">0C</st1:chmetcnv>0"</span>等。<span><br>11): </span>界面风格要保持一致，字的大小、颜色、字体要相同，除非是需要艺术处理或有特殊要求的地方。<span><br>12): </span>如果窗体支持最小化和最大化或放大时，窗体上的控件也要随着窗体而缩放；切忌只放大窗体而忽略控件的缩放。<span><br>13)</span>：对于含有按钮的界面一般不应该支持缩放，即右上角只有关闭功能。<span><br>14): </span>通常父窗体支持缩放时，子窗体没有必要缩放。<span><br>15)</span>：如果能给用户提供自定义界面风格则更好，由用户自己选择颜色、字体等。</span><span><br></span><strong><span>6</span></strong><strong><span>：菜单位置：</span></strong><span><br></span><span>菜单是界面上最重要的元素，菜单位置按照按功能来组织。</span><span><br></span><span>菜单测试细则：<span><br>1): </span>菜单通常采用&#8220;常用<span>--</span>主要<span>--</span>次要<span>--</span>工具<span>--</span>帮助&#8221;的位置排列，符合流行的<span>Windows</span>风格。<span><br>2): </span>常用的有&#8220;文件&#8221;、&#8220;编辑&#8221;，&#8220;查看&#8221;等，几乎每个系统都有这些选项，当然要根据不同的系统有所取舍。<span><br>3): </span><span>下拉菜单要根据菜单选项的含义进行分组，并且按照一定的规则进行排列，用横线隔开。<span><br></span></span><span>4): </span><span>一组菜单的使用有先后要求或有向导作用时，应该按先后次序排列。</span><span><br>5): </span><span>没有顺序要求的菜单项按使用频率和重要性排列，常用的放在开头，不常用的靠后放置；重要的放在开头，次要的放在后边。<span><br></span></span><span>6): </span><span>如果菜单选项较多，应该采用加长菜单的长度而减少深度的原则排列。</span><span><br>7): </span>菜单深度一般要求最多控制在三层以内。<span><br>8): </span>对常用的菜单要有快捷命令方式，组合原则见<span>8</span>。<span><br>9):<span> </span></span><span>对与进行的操作无关的菜单要用屏蔽的方式加以处理，如果采用动态加载方式——即只有需要的菜单才显示——最好。</span><span><br>10): </span>菜单前的图标不宜太大，与字高保持一直最好。<span><br>11): </span>主菜单的宽度要接近，字数不应多于四个，每个菜单的字数能相同最好。<span><br>12): </span>主菜单数目不应太多，最好为单排布置。</span></p>
<p align=left><span>13):</span><span>菜单条是否显示在合适的语境中？</span></p>
<p align=left><span>14):</span><span>应用程序的菜单条是否显示系统相关的特性（如时钟显示）？</span></p>
<p align=left><span>15):</span><span>下拉式操作能正确工作吗？</span></p>
<p align=left><span>16):</span><span>菜单、调色板和工具条是否工作正确？</span></p>
<p align=left><span>17):</span><span>是否适当地列出了所有的菜单功能和下拉式子功能？</span></p>
<p align=left><span>18):</span><span>是否可能通过鼠标访问所有的菜单功能？</span><span> </span></p>
<p align=left><span>19):</span><span>相同功能按钮的图标和文字是否一致？</span></p>
<p align=left><span>20):</span><span>是否能够用其它的文本命令激活每个菜单功能？</span></p>
<p align=left><span>21):</span><span>菜单功能是否随当前的窗口操作加亮或变灰？</span></p>
<p align=left><span>22):</span><span>菜单功能是否正确执行？</span></p>
<p align=left><span>23):</span><span>菜单功能的名字是否具有自解释性？</span></p>
<p align=left><span>24):</span><span>菜单项是否有帮助，是否语境相关？</span></p>
<p align=left><span>25):</span><span>在整个交互式语境中，是否可以识别鼠标操作？</span></p>
<p align=left><span>26):</span><span>如果要求多次点击鼠标，是否能够在语境正确识别？</span></p>
<p align=left><span>27):</span><span>如果鼠标有多个按钮，是否能够在语境中正确识别？</span></p>
<p align=left><span>28):</span><span>光标、处理指示器和识别指针是否随操作恰当地改变？</span><span><span>&nbsp;&nbsp; </span><br></span><strong><span>7:</span></strong><strong><span>独特性：</span></strong><span><br></span><span>如果一味的遵循业界的界面标准，则会丧失自己的个性<span>.</span>在框架符合以上规范的情况下，设计具有自己独特风格的界面尤为重要。<span>尤其在商业软件流通中有着很好的迁移默化的广告效用。<span><br></span></span>测试细则：<span><br>1):</span>　安装界面上应有单位介绍或产品介绍，并有自己的图标。<span><br>2):</span>　主界面，最好是大多数界面上要有公司图标。<span><br>3):</span>　登录界面上要有本产品的标志，同时包含公司图标。<span><br>4):</span>　帮助菜单的&#8220;关于&#8221;中应有版权和产品信息。<span><br>5):</span>　公司的系列产品要保持一直的界面风格，如背景色、字体、菜单排列方式、图标、安装过程、按钮用语等应该大体一致。</span></p>
<p align=left><strong><span>8</span></strong><strong><span>：快捷方式的组合</span></strong><span><br></span><span>在菜单及按钮中使用快捷键可以让喜欢使用键盘的用户操作得更快一些在西文<span>Windows</span>及其应用软件中快捷键的使用大多是一致的。 <span><br></span>菜单中：<span><br>1):</span>面向事务的组合有<span>: <br>Ctrl-D </span>删除 ；<span>Ctrl-F </span>寻找 ；<span>Ctrl </span>&#8211;<span>H</span>替换；<span>Ctrl-I </span>插入 ；<span>Ctrl-N </span>新记录 ；<span>Ctrl-S </span>保存<span> Ctrl-O </span>打开。<span><br>2)</span>：列表<span>: <br>Ctrl-R </span>，<span>Ctrl-G</span>定位；<span>Ctrl-Tab</span>下一分页窗口或反序浏览同一页面控件；。<span><br>3):</span>编辑<span>:<br>Ctrl-A</span>全选；<span>Ctrl-C </span>拷贝；<span>Ctrl-V </span>粘贴；<span>Ctrl-X </span>剪切；<span>Ctrl-Z</span>撤消操作；<span>Ctrl-Y</span>恢复操作。<span><br>4)</span>文件操作<span>:<br>Ctrl-P </span>打印；<span>Ctrl-W </span>关闭。<span><br>5):</span>系统菜单<span><br>Alt-A</span>文件；<span>Alt-E</span>编辑；<span>Alt-T</span>工具；<span>Alt</span>－<span>W</span>窗口；<span>Alt</span>－<span>H</span>帮助。<span><br>6):MS Windows</span>保留键<span>:<br>Ctrl-Esc </span>任务列表 ；<span>Ctrl-F4 </span>关闭窗口；<span> Alt-F4 </span>结束应用；<span>Alt-Tab </span>下一应用 ；<span>Enter </span>缺省按钮<span>/</span>确认操作 ；<span>Esc </span>取消按钮<span>/</span>取消操作；<span>Shift-F1 </span>上下文相关帮助。<span><br></span>按钮中：<span><br></span>可以根据系统需要而调节，以下只是常用的组合。<span><br>Alt-Y</span>确定<span>(</span>是<span>)</span>；<span>Alt-C</span>取消；<span>Alt-N </span>否；<span>Alt-D</span>删除；<span>Alt-Q</span>退出；<span>Alt-A</span>添加；<span>Alt-E</span>编辑；<span>Alt-B</span>浏览；<span>Alt-R</span>读；<span>Alt-W</span>写。<span><br></span>这些快捷键也可以作为开发中文应用软件的标准<span>,</span>但亦可使用汉语拼音的开头字母。</span><span> <span><br></span></span><strong><span>9:</span></strong><strong><span>安全性考虑：</span></strong><span><br></span><span>在界面上通过下列方式来控制出错几率，会大大减少系统因用户人为的错误引起的破坏。开发者应当尽量周全地考虑到各种可能发生的问题<span>,</span>使出错的可能降至最小。如应用出现保护性错误而退出系统<span>,</span>这种错误最容易使用户对软件失去信心。因为这意味着用户要中断思路<span>,</span>并费时费力地重新登录<span>,</span>而且已进行的操作也会因没有存盘而全部丢失。<span><br></span>安全性细则：<span><br>1)</span>：最重要的是排除可能会使应用非正常中止的错误。<span><br>2)</span>：应当注意尽可能避免用户无意录入无效的数据。<span><br>3)</span>：<span>采用相关控件限制用户输入值的种类。<span><br></span></span><span>4)</span>：<span>当用户作出选择的可能性只有两个时<span>,</span>可以采用单选框。 <span><br></span></span><span>5)</span>：当选择的可能再多一些时<span>,</span>可以采用复选框<span>,</span>每一种选择都是有效的<span>,</span>用户不可能输入任何一种无效的选择。<span><br>6)</span>：<span>当选项特别多时，可以采用列表框，下拉式列表框。<span><br></span></span><span>7)</span>：<span>在一个应用系统中<span>,</span>开发者应当避免用户作出未经授权或没有意义的操作。<span><br></span></span><span>8)</span>：<span>对可能引起致命错误或系统出错的输入字符或动作要加限制或屏蔽。<span><br></span></span><span>9):</span><span>对可能发生严重后果的操作要有补救措施。通过补救措施用户可以回到原来的正确状态。<span><br></span></span><span>10):</span><span>对一些特殊符号的输入、与系统使用的符号相冲突的字符等进行判断并阻止用户输入该字符。<span><br></span></span><span>11)</span>：<span>对错误操作最好支持可逆性处理，如取消系列操作。<span><br></span></span><span>12):</span><span>在输入有效性字符之前应该阻止用户进行只有输入之后才可进行的操作。<span><br></span></span><span>13):</span></span><span>对可能造成等待时间较长的操作应该提供取消功能。<span><br></span></span><span>14)</span><span>：特殊字符常有；<span>;</span>&#8217;&#8221;<span>&gt;&lt;,</span>｀&#8216;：&#8220;［&#8221;｛、<span>\|</span>｝］<span>+=)-(_*&amp;&amp;^%$#@!<br>,.</span>。<span>?/</span>还有空格。<span><br>15)</span>：<span>与系统采用的保留字符冲突的要加以限制。<span><br></span></span><span>16)</span>：<span>在读入用户所输入的信息时，根据需要选择是否去掉前后空格。<span><br></span></span><span>17)</span>：</span><span>有些读入数据库的字段不支持中间有空格，但用户切实需要输入中间空格，这时要在程序中加以处理。<br></span><span><br><br>以上关点烧鱼在补充下几点应特别注意：&nbsp; </p>
<p align=left><span>统</span><span> </span><span>一</span><span> </span><span>的</span><span> </span><span>外</span><span> </span><span>观；</span><span> </span></p>
<p align=left><span>引</span><span> </span><span>用</span><span> </span><span>对</span><span> </span><span>象</span><span> </span><span>前</span><span> </span><span>的</span><span> </span><span>有</span><span> </span><span>效</span><span> </span><span>性</span><span> </span><span>检</span><span> </span><span>验；</span><span> </span></p>
<p align=left><strong><span>使</span><span> </span><span>用</span><span> </span><span>正</span><span> </span><span>确</span><span> </span><span>的</span><span> </span><span>数</span><span> </span><span>据</span><span> </span><span>窗</span><span> </span><span>口</span><span> </span><span>列</span><span> </span><span>名；</span></strong><span> </span></p>
<p align=left><span>使</span><span> </span><span>用</span><span> </span><span>适</span><span> </span><span>当</span><span> </span><span>的</span><span>GUI </span><span>控</span><span> </span><span>件</span><span> </span><span>来</span><span> </span><span>使</span><span> </span><span>得</span><span> </span><span>用</span><span> </span><span>户</span><span> </span><span>输</span><span> </span><span>入有</span><span> </span><span>效；</span><span> </span></p>
<p align=left><strong><span>需</span><span> </span><span>要</span><span> </span><span>时</span><span> </span><span>对</span><span> </span><span>数</span><span> </span><span>据</span><span> </span><span>窗</span><span> </span><span>口</span><span> </span><span>的</span><span> </span><span>列</span><span> </span><span>作</span><span> </span><span>保</span><span> </span><span>护；</span></strong><span> <span><span>&nbsp;&nbsp;&nbsp;</span>//</span></span><span>如设置</span><span>TabOrder=0</span><span>等</span></p>
<p align=left><strong><span>提</span><span> </span><span>交</span><span> </span><span>前</span><span> </span><span>对</span><span> </span><span>数</span><span> </span><span>据</span><span> </span><span>窗</span><span> </span><span>口</span><span> </span><span>列</span><span> </span><span>作</span><span> </span><span>约</span><span> </span><span>束</span><span> </span><span>检</span><span> </span><span>验；</span></strong><span> </span></p>
<p align=left><span>避</span><span> </span><span>免</span><span> </span><span>用</span><span> </span><span>户</span><span> </span><span>进</span><span> </span><span>入</span><span> </span><span>危</span><span> </span><span>险</span><span> </span><span>地</span><span> </span><span>带；</span><span> </span></p>
<p align=left><span>对</span><span> </span><span>用</span><span> </span><span>户</span><span> </span><span>不</span><span> </span><span>经</span><span> </span><span>意</span><span> </span><span>的</span><span> </span><span>操</span><span> </span><span>作</span><span> </span><span>作</span><span> </span><span>出</span><span> </span><span>提</span><span> </span><span>示。</span><span> </span></p>
<p align=left><br></span><strong><span>10:</span></strong><strong><span>多窗口的应用与系统资源：</span></strong><span><br></span><span>设计良好的软件不仅要有完备的功能，而且要尽可能的占用最底限度的资源。</span><span><br></span><span>1)</span><span>：在多窗口系统中，有些界面要求必须保持在最顶层，避免用户在打开多个窗口时，不停的切换甚至最小化其它窗口来显示该窗口。<span><br>2)</span>：在主界面载入完毕后自动卸出内存，让出所占用的<span>WINDOWS</span>系统资源。<span><br>3)</span>：关闭所有窗体，系统退出后要释放所占的所有系统资源 ，除非是需要后台运行的系统。<span><br>4)</span>：尽量防止对系统的独占使用。</span></p>
<p align=left><span>5)</span><span>：</span><span>窗口能否基于相关的输入或菜单命令适当地打开？</span></p>
<p align=left><span>6)</span><span>：</span><span>窗口能否改变大小、移动和滚动？</span></p>
<p align=left><span>7)</span><span>：</span><span>窗口中的数据内容能否使用鼠标、功能键、方向箭头和键盘访问？</span></p>
<p align=left><span>8)</span><span>：</span><span>当被覆盖并重调用后，窗口能否正确地再生？</span></p>
<p align=left><span>9)</span><span>：</span><span>需要时能否使用所有窗口相关的功能？</span></p>
<p align=left><span>10)</span><span>：</span><span>所有窗口相关的功能是可操作的吗？</span></p>
<p align=left><span>11)</span><span>：</span><span>是否有相关的下拉式菜单、工具条、滚动条、对话框、按钮、图标和其它控制可为窗口可用，并适当地显示？</span></p>
<p align=left><span>12)</span><span>：</span><span>显示多个窗口时，窗口的名称是否被适当地表示？</span></p>
<p align=left><span>13)</span><span>：</span><span>活动窗口是否被适当地加亮？</span></p>
<p align=left><span>14)</span><span>：</span><span>如果使用多任务，是否所有的窗口被实时更新？</span></p>
<p align=left><span>15)</span><span>：</span><span>多次或不正确按鼠标是否会导致无法预料的副作用？</span></p>
<p align=left><span>16)</span><span>：</span><span>窗口的声音和颜色提示和窗口的操作顺序是否符合需求？</span></p>
<p align=left><span>17)</span><span>：</span><span>窗口是否正确地关闭？</span></p>
<div id=digg_block>
<div id=div_digg></div>
</div>
</div>
<img src ="http://www.cnitblog.com/stomic/aggbug/62170.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2009-10-28 01:42 <a href="http://www.cnitblog.com/stomic/archive/2009/10/28/62170.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试基础-测试类型</title><link>http://www.cnitblog.com/stomic/archive/2009/10/13/61787.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Tue, 13 Oct 2009 02:18:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2009/10/13/61787.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/61787.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2009/10/13/61787.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/61787.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/61787.html</trackback:ping><description><![CDATA[<p><strong>&nbsp; </p>
<p align=left><st1:chsdate Year="1899" Month="12" Day="30" IsLunarDate="False" IsROCDate="False" w:st="on"><span>7.1.1</span></st1:chsdate><span> 从测试设计的方法分类</span></p>
<p align=left><span>测试设计有两类方法：</span><span>Black box</span><span>（黑箱）、</span><span>White box</span><span>（白箱）。</span><span> </span></p>
<p align=left><span>这是每个接触过软件测试的人都会给出的答案。但这只是整个软件测试的入门知识。可以跳过去，直接讨论下面的内容。</span><span> </span></p>
<p align=left><span>问：我在网上看到有人争论黑箱测试和白箱测试哪一个是另一个的基础，还有哪一个更难，哪一个更有前途，等等。据说河曲数码在搞</span><span>&#8220;</span><span>灰箱测试</span><span>&#8221;</span><span>，是不是更高级？能不能简单讲一讲？</span><span> </span></p>
<p align=left><span>阿超：大家都有这些问题么？</span><span> </span></p>
<p align=left><span>杂曰：</span><span>[</span><span>略去对此问题热烈的争论</span><span>500</span><span>字</span><span>] </span></p>
<p align=left><span>阿超：听了大家的争论，看来我们的确得花不少时间统一认识。</span><span> </span><span>所谓黑箱</span><span>/</span><span>白箱，是指软件测试设计的方法，不是软件测试的方法！注意</span><span>&#8220;</span><span>设计</span><span>&#8221;</span><span>二字。</span><span> </span></p>
<p align=left><span>黑箱：在设计测试的过程中，把软件系统当作一个</span><span>&#8220;</span><span>黑箱</span><span>&#8221;</span><span>，无法了解或使用系统的内部结构及知识。一个更准确的说法是</span><span>&#8220;Behavioral&nbsp; Test Design&#8221;</span><span>，从软件的行为，而不是内部结构出发来设计测试。</span><span><br><br></span><span>白箱：在设计测试的过程中，设计者可以</span><span>&#8220;</span><span>看到</span><span>&#8221;</span><span>软件系统的内部结构，并且使用软件的内部知识来指导测试数据及方法的选择。</span><span>&#8220;</span><span>白箱</span><span>&#8221;</span><span>并不是一个精确的说法，因为把箱子涂成白色，同样也看不见箱子里的东西。有人建议用</span><span>&#8220;</span><span>玻璃箱</span><span>&#8221;</span><span>来表示。</span><span> </span></p>
<p align=left><span>在实际工作中，我们不应画地为牢，严格只用某一种方法来设计测试方法。在实际的测试中，当然是对系统了解得越多越好。所谓</span><span>&#8220;</span><span>灰箱</span><span>&#8221;</span><span>的提法，正是这一反映。有些测试专家甚至希望我们忘记全部的</span><span>&#8220;</span><span>箱子</span><span>&#8221;</span><span>和它们的颜色。</span><span> </span></p>
<p align=left><span>问：如果我是一个开发者，我能做</span><span>&#8220;</span><span>黑箱</span><span>&#8221;</span><span>么？</span><span> </span></p>
<p align=left><span>答：并不是要禁止懂得程序内部结构的人员来进行黑箱测试设计，只不过是在设计时有意不考虑软件的内部结构。例如：在测试程序内部基本模块的时候（单元测试），通常要求由对程序结构非常了解的程序员来设计，这是因为内部模块的</span><span>&#8220;</span><span>行为</span><span>&#8221;</span><span>和程序的外部功能并没有直接的关系，而且内部基</span><span> </span><span>本模块的</span><span>&#8220;</span><span>行为</span><span>&#8221;</span><span>通常没有明确的定义。另一个例子是</span><span>&#8220;</span><span>易用性测试</span><span>&#8221;</span><span>，在设计此类测试的时候，没必要纠缠于程序的内部结构，而是着重于软件的界面和行为。但是软件易用性测试也需要很多专业知识。这也从一个侧面表明</span><span>&#8220;</span><span>黑箱</span><span>&#8221;</span><span>和</span><span>&#8220;</span><span>白箱</span><span>&#8221;</span><span>没有简单的高低之分。</span><span><br><br></span><span>一旦测试用例写出来之后，大可以忘了它们是从哪种颜色的箱子里出来的，用它就可以了。</span><span> </span></p>
<p align=left><span>问：有人说</span><span>&#8220;</span><span>黑箱</span><span>&#8221;</span><span>，有人说</span><span>&#8220;</span><span>黑盒</span><span>&#8221;</span><span>，到底是</span><span>&#8220;</span><span>箱子</span><span>&#8221;</span><span>还是</span><span>&#8220;</span><span>盒子</span><span>&#8221;</span><span>？</span><span> </span></p>
<p align=left><span>答：在网上搜索了一下，</span><span>&#8220;</span><span>黑箱测试</span><span>&#8221;</span><span>有超过</span><span> 100 </span><span>万个记录，</span><span>&#8220;</span><span>黑盒测试</span><span>&#8221;</span><span>只有</span><span>70</span><span>多万。所以</span><span>&#8220;</span><span>箱子</span><span>&#8221;</span><span>赢了。</span><span> </span><span>问：但是我听九条说他刚进公司实习的时候只能做</span><span>&#8220;</span><span>黑箱测试</span><span>&#8221;</span><span>，这是什么意思？</span><span> </span></p>
<p align=left><span>九条：我刚到公司实习的时候，两眼一摸黑，看到啥都是</span><span>&#8220;</span><span>黑箱</span><span>&#8221;</span><span>，即使测试用例是由懂得程序结构的开发人员写出来的，我还是只会机械地运行。我是知其然，不知其所以然，箱子当然是黑的。后来看得多了，学了一些东西，能够了解程序的结构和算法，箱子的颜色就变浅了，好像能看到箱子里的东西一样。</span></p>
<p>&nbsp;</p>
<p align=left><st1:chsdate Year="1899" Month="12" Day="30" IsLunarDate="False" IsROCDate="False" w:st="on"><span>7.1.2</span></st1:chsdate><span> 从测试的目的分类</span></p>
<p align=left><span>1．功能测试</span></p>
<p align=left><span>以下的测试术语主要是测试软件的功能。在表</span><span>7-1</span><span>所列的测试中，测试的范围由小到大，测试者也由内到外</span><span>——</span><span>从程序开发人员（单元测试）到测试人员，到一般用户（</span><span>Alpha/Beta</span><span>测试）。</span><span> </span></p>
<p align=left><span>表</span><span>7-1&nbsp; </span><span>功能测试分类</span><span>&nbsp;<br>
<table class=ln cellSpacing=0 borderColorDark=#ffffff align=center bgColor=#ddddd borderColorLight=#999999 border=1>
    <tbody>
        <tr>
            <td bgColor=#ffffff><a href="http://new.51cto.com/files/uploadimg/20090205/154727492.jpg" target=_blank><img class=fit-image onmousewheel="javascript:return big(this)" style="WIDTH: 498px" height=148 alt="" src="http://images.51cto.com/files/uploadimg/20090205/154727492.jpg" width=603 onload="javascript:if(this.width>498)this.style.width=498;" border=0 src_cetemp="http://images.51cto.com/files/uploadimg/20090205/154727492.jpg"></a></td>
        </tr>
    </tbody>
</table>
<span><span lang=EN-US style="FONT-SIZE: 9pt; COLOR: black; FONT-FAMILY: ˎ̥; mso-fareast-font-family: 宋体; mso-bidi-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><a href="http://new.51cto.com/files/uploadimg/20090205/154727492.jpg" target=_blank><span style="TEXT-DECORATION: none; text-underline: none"><v:shapetype id=_x0000_t75 coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><font color=#0000ff><font face="宋体, MS Song"><v:stroke joinstyle="miter"></v:stroke></font></font><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></v:path><o:lock v:ext="edit" aspectratio="t"></o:lock></v:shapetype></span></a></span><br>续表 <br>
<table class=ln cellSpacing=0 borderColorDark=#ffffff align=center bgColor=#ddddd borderColorLight=#999999 border=1>
    <tbody>
        <tr>
            <td bgColor=#ffffff><a href="http://new.51cto.com/files/uploadimg/20090205/154753437.jpg" target=_blank><img class=fit-image onmousewheel="javascript:return big(this)" style="WIDTH: 498px" height=114 alt="" src="http://images.51cto.com/files/uploadimg/20090205/154753437.jpg" width=607 onload="javascript:if(this.width>498)this.style.width=498;" border=0 src_cetemp="http://images.51cto.com/files/uploadimg/20090205/154753437.jpg"></a>&nbsp;</td>
        </tr>
    </tbody>
</table>
<br><span>2．非功能测试</span><span> </span></span></span></p>
<p align=left><span>一个软件除了基本功能之外，还有很多功能之外的特性，这些叫</span><span>&#8220;non-functional requirement&#8221;</span><span>，或者</span><span>&#8220;quality of service requirement&#8221;——</span><span>服务质量需求。没有软件的功能，这些特性都无从表现出来，因此，我们要在软件开发的适当阶段</span><span>——</span><span>基本功能完成后做这些测试。</span><span><br><br></span><span>如表</span><span>7-2</span><span>所示：</span><span> </span></p>
<p align=left><span>表</span><span>7-2&nbsp; </span><span>非功能测试</span><span> <br>
<table class=ln cellSpacing=0 borderColorDark=#ffffff align=center bgColor=#ddddd borderColorLight=#999999 border=1>
    <tbody>
        <tr>
            <td bgColor=#ffffff><a href="http://new.51cto.com/files/uploadimg/20090205/154819465.jpg" target=_blank><img class=fit-image onmousewheel="javascript:return big(this)" style="WIDTH: 498px" height=315 alt="" src="http://images.51cto.com/files/uploadimg/20090205/154819465.jpg" width=606 onload="javascript:if(this.width>498)this.style.width=498;" border=0 src_cetemp="http://images.51cto.com/files/uploadimg/20090205/154819465.jpg"></a></td>
        </tr>
    </tbody>
</table>
</span><st1:chsdate Year="1899" Month="12" Day="30" IsLunarDate="False" IsROCDate="False" w:st="on"><strong><span><br>7.1.3</span></strong></st1:chsdate><strong><span> </span></strong><strong><span>按测试的时机和作用分类</span></strong></p>
<p align=left><span>在开发软件的过程中，不少测试起着</span><span>&#8220;</span><span>烽火台</span><span>&#8221;</span><span>的作用，它们告诉我们软件开发的流程是否顺畅，这些测试如表</span><span>7-3</span><span>所示：</span></p>
<p align=left><span>表</span><span>7-3&nbsp; </span><span>烽火台<br>
<table class=ln cellSpacing=0 borderColorDark=#ffffff align=center bgColor=#ddddd borderColorLight=#999999 border=1>
    <tbody>
        <tr>
            <td bgColor=#ffffff><a href="http://new.51cto.com/files/uploadimg/20090205/154908390.jpg" target=_blank><img class=fit-image onmousewheel="javascript:return big(this)" style="WIDTH: 498px" height=120 alt="" src="http://images.51cto.com/files/uploadimg/20090205/154908390.jpg" width=604 onload="javascript:if(this.width>498)this.style.width=498;" border=0 src_cetemp="http://images.51cto.com/files/uploadimg/20090205/154908390.jpg"></a></td>
        </tr>
    </tbody>
</table>
</span><span><br>另一些测试名称，则是说明不同的测试方法，如表</span><span>7-4</span><span>所示：</span></p>
<p align=left><span>表</span><span>7-4&nbsp; </span><span>不同测试方法<br>
<table class=ln cellSpacing=0 borderColorDark=#ffffff align=center bgColor=#ddddd borderColorLight=#999999 border=1>
    <tbody>
        <tr>
            <td bgColor=#ffffff><a href="http://new.51cto.com/files/uploadimg/20090205/154929939.jpg" target=_blank><img class=fit-image onmousewheel="javascript:return big(this)" style="WIDTH: 498px" height=217 alt="" src="http://images.51cto.com/files/uploadimg/20090205/154929939.jpg" width=598 onload="javascript:if(this.width>498)this.style.width=498;" border=0 src_cetemp="http://images.51cto.com/files/uploadimg/20090205/154929939.jpg"></a></td>
        </tr>
    </tbody>
</table>
</span><strong><span><br>7.2&nbsp; </span></strong><strong><span>单元测试（</span></strong><strong><span>Unit Test</span></strong><strong><span>）</span></strong></p>
<p align=left><span>二柱：我们也试过用单元测试来保证质量，要求每人都要写，在签入代码前必须通过单元测试。但是搞了几个星期就不了了之。</span></p>
<p align=left><span>大家七嘴八舌地列举了单元测试的问题：</span></p>
<p align=left><span>◆</span>&nbsp;<span>有时单元测试报了错，再运行一次就好了，后来大家就不想花时间改错，多运行几次，有一次通过就行了；</span></p>
<p align=left><span>◆</span>&nbsp;<span>单元测试中好多错都和环境有关，在别人的机器都运行不成功；</span></p>
<p align=left><span>◆</span>&nbsp;<span>花在单元测试上的时间要比写代码的时间还多，提高代码覆盖率到</span><span>90%</span><span>以上太难了；</span></p>
<p align=left><span>◆</span>&nbsp;<span>单元测试中我们还要测试效能和压力，花了很多时间；</span></p>
<p align=left><span>◆</span>&nbsp;<span>我们都这么费劲地测了，那还要测试人员干什么？</span></p>
<p align=left><span>阿超：看来问题还不少，我们留到后面再谈（见后面</span><span>&#8220;</span><span>单元测试</span><span>&#8221;</span><span>的具体描述）。</span></p>
<p>&nbsp;</p>
<p align=left><strong><span>7.3&nbsp; </span></strong><strong><span>代码覆盖率测试（</span></strong><strong><span>Code Coverage Analysis</span></strong><strong><span>）</span></strong></p>
<p align=left><span>前面单元测试中提到代码覆盖率，简单来说代码被执行过，就是</span><span>&#8220;</span><span>覆盖过</span><span>&#8221;</span><span>，如果一段程序运行了一组测试用例之后，</span><span>100%</span><span>的代码被执行了，那么是否就说明再也不用写新的测试用例了呢？</span></p>
<p align=left><span>答案是否定的。</span></p>
<p align=left><span>（</span><span>1</span><span>）不同代码是否执行，有很多组合，一行代码被执行过，没有问题，并不表明这一行程序在所有可能条件的组合下都能正确无误地运行。</span></p>
<p align=left><span>（</span><span>2</span><span>）代码覆盖不能测出还没有写的代码（缺少的逻辑）导致的错误。</span></p>
<p align=left><span>比如：</span></p>
<p align=left><span>a. </span><span>没有检查过程调用的返回值；</span></p>
<p align=left><span>b. </span><span>没有释放资源。</span></p>
<p align=left><span>（</span><span>3</span><span>）代码覆盖不能测出效能问题。</span></p>
<p align=left><span>（</span><span>4</span><span>）代码覆盖不能测出时序问题，由时序导致的程序错误（例如：线程之间的同步）。</span></p>
<p align=left><span>（</span><span>5</span><span>）代码中和用户界面相关的功能不能简单地以代码覆盖率来衡量优劣。</span></p>
<p>&nbsp;</p>
<strong><span>
<p><strong>7.4&nbsp; 构建验证测试（BVT：Build Verification Test）</strong></p>
<p>望文生义，构建验证测试是指在一个构建完成之后，团队自动运行的一套验证系统基本功能的测试。在大多数情况下，这一套系统都是在自动构建成功后自动运行的，有些情况下也会手工运行，但是由于构建是自动生成的，我们也要努力让BVT自动运行。</p>
<p>问：一个系统有这么多功能点，什么是基本的功能，什么不是基本的功能？</p>
<p>答：第一，必须能安装；第二，必须能够实现一组核心场景。例如，对于字处理软件来说，必须能打开/编辑/保存一个文档文件，但是一些高级功能，如文本自动纠错，则不在其中；又如，对于网站系统，用户可以注册/上传/下载信息，但是一些高级功能，如删除用户，列出用户参与的所有讨论，则不在其中。</p>
<p>在运行 BVT 之前，可以运行所有的单元测试，这样可以保证系统的单元测试和程序员的单元测试版本一致。在不少情况下，开发人员修改了程序和单元测试，但是忘了把修改过的单元测试也同时签入源代码库中。</p>
<p>通过BVT的构建可以称为可测（Self-test），意思是说团队可以用这一版本进行各种测试，因为它的基本功能都是可用的。通不过 BVT 的构建称为&#8220;不可测&#8221;（Self-hosed）。</p>
<p>如果构建测试不能通过，那么自动测试框架会自动对每一个失败的测试产生一个 Bug（小强）。一般的做法下这些小强都有最高优先级，开发人员要首先修改这些小强。大家知道维持每日构建，并产生一个可测的版本是软件开发过程中质量控制的基础。对于导致问题的小强，我们该怎么办？答案是——</p>
<p>（1）找到导致失败的原因，如果原因很简单，程序员可以马上修改，然后直接提交。</p>
<p>（2）找到导致失败的原因的修改集，把此修改集剔出此版本（程序员必须修改好后再重新提交到源代码库中）。</p>
<p>（3）程序员必须在下一个构建开始前把此小强修理好。</p>
<p>方法（1）和（2）都可以使今天的构建成为&#8220;可测&#8221;，但是有时各方面的修改互相依赖，不能在短时间内解决所有问题，那就只能采用第三种方法了。</p>
<p>问：有人提到一种&#8220;Smoke Test&#8221;，冒烟测试，是怎么回事？</p>
<p>答：事实上这是一种基本验证测试，据说是从硬件设计行业流传过来的说法。当年设计电路板的时候，很多情况下，新的电路板一插上电源就冒起白烟，烧坏了，如果插上电源后没有冒烟，那就是通过了&#8220;冒烟测试&#8221;，可以进一步测试电路板的功能了。我们正在讨论的BVT也是一种冒烟测试。</p>
</span></strong>
<p>&nbsp;</p>
<p align=left><strong><span>7.5&nbsp; </span></strong><strong><span>验收测试（</span></strong><strong><span>Acceptance Test</span></strong><strong><span>）</span></strong></p>
<p align=left><span>测试团队拿到一个</span><span>&#8220;</span><span>可测</span><span>&#8221;</span><span>等级的构建，他们就会按照测试计划，测试各自负责的模块和功能，这个过程可能会产生总共</span><span> 10 </span><span>来个</span><span> Bug</span><span>，也可能产生</span><span>100</span><span>个以上的</span><span>Bug</span><span>，那么如何保证我们有效地测试软件，同时我们在这一步怎样衡量构建的质量？</span></p>
<p align=left><span>在</span><span>MSF</span><span>敏捷模式中，我们建议还是采用场景来规划测试工作。</span></p>
<p align=left><span>在</span><span>&#8220;</span><span>基本场景</span><span>&#8221;</span><span>的基础上，把所有系统理论上目前支持的场景都列出来，然后按功能分类测试，如果测试成功，就在此场景中标明</span><span>&#8220;</span><span>成功</span><span>&#8221;</span><span>，否则，就标明</span><span>&#8220;</span><span>失败</span><span>&#8221;</span><span>，并且把失败的情况用一个（或几个）</span><span>&#8220;</span><span>小强</span><span>&#8221;/Bug</span><span>来表示。当所有的测试人员完成对场景的测试，我们自然地就得出了表</span><span>7-5</span><span>。</span></p>
<p align=left><span>表</span><span>7-5&nbsp; </span><span>场景测试报告<br>
<table class=ln cellSpacing=0 borderColorDark=#ffffff align=center bgColor=#ddddd borderColorLight=#999999 border=1>
    <tbody>
        <tr>
            <td bgColor=#ffffff><a href="http://new.51cto.com/files/uploadimg/20090205/155421862.jpg" target=_blank><img class=fit-image onmousewheel="javascript:return big(this)" style="WIDTH: 498px" height=151 alt="" src="http://images.51cto.com/files/uploadimg/20090205/155421862.jpg" width=605 onload="javascript:if(this.width>498)this.style.width=498;" border=0 src_cetemp="http://images.51cto.com/files/uploadimg/20090205/155421862.jpg"></a></td>
        </tr>
    </tbody>
</table>
</span><span><br>这样就能很快地报告</span><span>&#8220;</span><span>功能测试</span><span>56%</span><span>通过</span><span>&#8221;</span><span>等。如果所有场景都能通过，（有些情况下可以把此标准从</span><span> 100%</span><span>降低到</span><span> 90%</span><span>左右）则这个构建的质量是</span><span>&#8220;</span><span>可用</span><span>&#8221;</span><span>，意味着这一个版本可以给用户使用。在这种情况下，客户、合作伙伴可以得到这样的版本，这也是所谓</span><span>&#8220;</span><span>技术预览版</span><span>&#8221;</span><span>或</span><span>&#8220;</span><span>社区预览版</span><span>&#8221;</span><span>的由来。</span></p>
<p align=left><span>但是，有一个重要的问题要大家注意：</span><span>&#8220;</span><span>可用</span><span>&#8221;</span><span>，并不是指软件的所有功能都没有问题，而是指在目前的用户场景中，按照场景的要求进行操作，都能得到预期的效果，注意以下两种情况：</span></p>
<p align=left><span>（</span><span>1</span><span>）在目前还没有定义的用户场景中，程序质量如何，还未得而知。例如：场景中没有考虑到多种语言设置。</span></p>
<p align=left><span>（</span><span>2</span><span>）不按照场景的要求进行的操作，结果如何，还未得而知。如：在某一场景中，场景规定用户可以在最后付款前取消操作，回到上一步，如果一个测试人员发现在反复提交</span><span>/</span><span>取消同一访问多次后，网页出现问题，这并不能说明用户场景失败，当然对于这个极端的</span><span>Bug</span><span>，也必须找出原因并在适当的时间改正。</span></p>
<p align=left><span>这种测试有时也被称为验收测试</span><span>&#8220;Acceptance Test&#8221;</span><span>，因为如果构建通过了这样的测试，这一个构建就被测试团队</span><span>&#8220;</span><span>接受了</span><span>&#8221;</span><span>。同时，还有对系统各个方面进行的</span><span>&#8220;</span><span>验收</span><span>&#8221;</span><span>测试，如系统的全球化验收测试，或者针对某一语言环境、某一个平台做的测试。</span></p>
<p>&nbsp;</p>
<p align=left><strong><span>7.6&nbsp; "</span></strong><strong><span>探索式</span></strong><strong><span>"</span></strong><strong><span>的测试（</span></strong><strong><span>Ad hoc Test</span></strong><strong><span>）</span></strong><span> </span></p>
<p align=left><span>&#8220;Ad hoc&#8221;</span><span>原意是指</span><span>&#8220;</span><span>特定的，一次性的</span><span>&#8221;</span><span>。这样的测试也可以叫</span><span>Exploratory Test</span><span>。</span></p>
<p align=left><span>什么叫</span><span>&#8220;</span><span>特定</span><span>&#8221;</span><span>测试？或者</span><span>&#8220;</span><span>探索式</span><span>&#8221;</span><span>的测试？</span></p>
<p align=left><span>就是为了某一个特定目的进行的测试，就这一次，以后一般也不会重复测试。在软件工程的实践中，</span><span>&#8220;Ad hoc&#8221;</span><span>大部分是指随机进行的、探索性的测试。</span></p>
<p align=left><span>比如：测试人员阿毛拿到了一个新的构建，按计划是进行模块</span><span>A</span><span>的功能测试，但是他灵机一动，想看看另一个功能</span><span>B</span><span>做得如何，或者想看看模块</span><span>A</span><span>在某种边界条件下会出现什么问题，于是他就</span><span>&#8220;Ad hoc&#8221;</span><span>一把，居然在这一功能模块中发现了不少小强。</span></p>
<p align=left><span>&#8220;Ad hoc&#8221;</span><span>也意味着测试是尝试性的，</span><span>&#8220;</span><span>我来试试，在这个对话框中一通乱按，然后随意改变窗口大小，看看会出什么问题</span><span>&#8230;&#8230;&#8221;</span><span>，如果没问题，那么以后也不会再这么做了。</span></p>
<p align=left><span>一般情况下，测试人员不会花很多时间进行特定测试，但是在一些缺乏管理的团队中，很多时候测试人员不知道自己此时应该做什么，只好做一些看似</span><span>&#8220;Ad hoc&#8221;</span><span>的测试，比如随机测试各个功能的各个方面。这些测试理论上都应该由测试管理人员规划好属于各个功能模块的测试用例。</span></p>
<p align=left><span>在一个团队中，</span><span>&#8220;Ad hoc&#8221;</span><span>太多是一个管理不好的标志，因为</span><span>&#8220;Ad hoc&#8221;</span><span>是指那些一时想到要做，但是以后也没有计划经常重复的测试计划。</span></p>
<p align=left><span>问：我听说有人是</span><span>&#8220;Ad hoc&#8221;</span><span>测试的高手，这是什么意思？</span></p>
<p align=left><span>答：有很多测试人员会按部就班地进行测试，但是还有一些人头脑比较灵活，喜欢另辟蹊径，测试一些一般人不会想到的场景，这些人往往会发现更多的小强。开发人员对这样的</span><span>&#8220;Ad hoc&#8221;</span><span>高手是又爱又恨。</span></p>
<p align=left><span>问：看问题要分两方面，有些</span><span>&#8220;Ad hoc&#8221;</span><span>发现的小强在正常使用软件中几乎不会出现，我们要不要花时间</span><span>&#8220;Adhoc&#8221;</span><span>？</span></p>
<p align=left><span>答：现在一些成功的通用软件的用户以百万计，按部就班的测试计划很难包括很多实际的场景，这时，</span><span>&#8220;Ad hoc&#8221;</span><span>测试能够发现重要的问题；另外一些风险很大的领域，例如安全性，一旦出了问题，威胁就会相当大，这时要多鼓励一些</span><span>&#8220;Ad hoc&#8221;</span><span>测试，以弥补普通测试的不足。从这个意义上说，</span><span>&#8220;Ad hoc&#8221;</span><span>测试可以用来衡量当前测试用例的完备性，如果你探索了半天，都没有发现什么在现有测试用例之外的问题，那就说明现有的测试用例是比较完备的。</span></p>
<p align=left><span>&#8220;Ad hoc&#8221;</span><span>测试的测试流程是不可重复的，因为它的测试都是</span><span>&#8220;</span><span>特定</span><span>&#8221;</span><span>测试，没法重复。由于这一原因，</span><span>&#8220;Ad hoc&#8221;</span><span>测试不能自动化，就这一点而言，还达不到</span><span>CMM</span><span>的第二级</span><span>——</span><span>可重复级。</span></p>
<p align=left><span>作为管理人员来说，如果太多的小强是在</span><span>&#8220;Ad hoc&#8221;</span><span>出来的，那我们就要看看测试计划是否基于实际的场景，开发人员的代码逻辑是否完善，等等。同时，要善于把看似</span><span>&#8220;Ad hoc&#8221;</span><span>的测试用例抽象出来，囊括到以后的测试计划中。</span></p>
<p>&nbsp;</p>
<p align=left><strong><span>7.7&nbsp; </span></strong><strong><span>回归测试（</span></strong><strong><span>Regression Test</span></strong><strong><span>）</span></strong></p>
<p align=left><span>问：我听说不少关于</span><span>Regression Test</span><span>的介绍，但是它到底是怎么</span><span>&#8220;</span><span>回归</span><span>&#8221;</span><span>法？回归到哪里去？我还是没搞懂。</span></p>
<p align=left><span>答：</span><span>Regress </span><span>的英语定义是：</span><span>&nbsp; return&nbsp; to&nbsp; a&nbsp; worse&nbsp; or&nbsp; lessdeveloped state</span><span>。是倒退、退化、退步的意思。</span></p>
<p align=left><span>在软件项目中，如果一个模块或功能以前是正常工作的，但是在一个新的构建中出了问题，那这个模块就出现了一个</span><span>&#8220;</span><span>退步</span><span>&#8221;</span><span>（</span><span>Regression</span><span>），从正常工作的稳定状态退化到不正常工作的不稳定状态。</span></p>
<p align=left><span>在一个模块的功能逐步完成的同时，与此功能有关的测试用例也同样在完善中。一旦有关的测试用例通过，我们就得到了此模块的功能基准（</span><span>Baseline</span><span>）。</span></p>
<p align=left><span>假如，在</span><st1:chsdate Year="1899" Month="12" Day="30" IsLunarDate="False" IsROCDate="False" w:st="on"><span>3.1.5</span></st1:chsdate><span>版本，模块</span><span>A</span><span>的测试用例</span><span>125</span><span>是通过的，但是测试人员发现在新的版本</span><span>3.1.6</span><span>，这个测试用例却失败了，这就是一个</span><span>&#8220;</span><span>倒退</span><span>&#8221;</span><span>。在新版本上运行所有已通过的测试用例以验证有没有</span><span>&#8220;</span><span>退化</span><span>&#8221;</span><span>情况发生，这个过程就是一个</span><span>&#8220;Regression Test&#8221;</span><span>。如果这样的</span><span>&#8220;</span><span>倒退</span><span>&#8221;</span><span>是由于模块的功能发生了正常变化（由于设计变更的原因）引起的，那么测试用例的基准就要修改，以便和新的功能保持一致。</span></p>
<p align=left><span>针对一个</span><span>Bug Fix</span><span>（拖鞋），我们也要作</span><span>Regression Test</span><span>。</span></p>
<p align=left><span>（</span><span>1</span><span>）验证新的代码的确把缺陷改正了。</span></p>
<p align=left><span>（</span><span>2</span><span>）同时要验证新的代码没有把模块的现有功能破坏，没有</span><span>Regression</span><span>。</span></p>
<p align=left><span>所以对于</span><span>&#8220;</span><span>回归测试</span><span>&#8221;</span><span>中的</span><span>&#8220;</span><span>回归</span><span>&#8221;</span><span>，我们可以理解为</span><span>&#8220;</span><span>回归到以前不正常的状态</span><span>&#8221;</span><span>。</span></p>
<p align=left><span>回归测试最好要自动化，因为这样就可以对于每一个构建快速运行所有回归测试，以保证尽早发现问题。在微软的实践中，在一个项目的最后稳定阶段，所有人都要参加测试，以验证所有已经修复过的</span><span> Bug </span><span>的确得到了修复，并且没有在最后一个版本中</span><span>&#8220;</span><span>复发</span><span>&#8221;</span><span>，这是一个大规模的、全面的</span><span>&#8220;</span><span>回归测试</span><span>&#8221;</span><span>。</span></p>
<p>&nbsp;</p>
<p align=left><strong><span>7.8&nbsp; </span></strong><strong><span>场景</span></strong><strong><span>/</span></strong><strong><span>集成</span></strong><strong><span>/</span></strong><strong><span>系统测试（</span></strong><strong><span>Scenario/ Integration / System Test</span></strong><strong><span>）</span></strong></p>
<p align=left><span>在软件开发的一定阶段，我们要对一个软件进行全面和系统的测试，以保证软件的各个模块都能共同工作，在各方面都能满足用户的要求。这时的测试叫系统</span><span>/</span><span>集成测试。</span></p>
<p align=left><span>问：有一种测试叫</span><span>Scenario Test</span><span>，是什么意思？</span></p>
<p align=left><span>答：就是以场景为驱动的集成测试，关于</span><span>&#8220;</span><span>场景</span><span>&#8221;</span><span>，大家可以看专门的介绍。这一方法的核心思想是：当用户使用一个软件的时候，他</span><span>/</span><span>她并不会独立使用各个模块，而是把软件作为一个整体来使用的。我们在做场景测试的时候，就需要考虑在现实环境中用户使用软件的流程是怎样的，然后模拟这个流程，看看软件能不能达到用户的需求。这样，能使软件符合用户使用的实际需求。</span></p>
<p align=left><span>以一个数字照片编辑软件为例，这个软件的各个模块都是独立开发的，可是用户有一定的典型流程，如果这个流程走得不好，哪怕某个模块的质量再高，用户也不会满意。用户的典型流程是：</span></p>
<p align=left><span>（</span><span>1</span><span>）把照相机的储存卡插入电脑。</span></p>
<p align=left><span>（</span><span>2</span><span>）程序会弹出窗口提示用户导入照片。</span></p>
<p align=left><span>（</span><span>3</span><span>）用户根据提示导入照片。</span></p>
<p align=left><span>（</span><span>4</span><span>）对照片进行快速编辑。</span></p>
<p align=left><span>a. </span><span>调整颜色；</span></p>
<p align=left><span>b. </span><span>调整亮度，对比度；</span></p>
<p align=left><span>c. </span><span>修改红眼。</span></p>
<p align=left><span>（</span><span>5</span><span>）选择其中几幅照片，用</span><span>E-mail</span><span>发送。</span></p>
<p align=left><span>这里面哪一步出了问题，都会影响用户对这一产品的使用。如果这里面各个模块的用户界面不一致（即使是</span><span>&#8220;</span><span>确认</span><span>&#8221; </span><span>和</span><span> &#8220;</span><span>取消</span><span>&#8221; </span><span>按钮的次序不同），用户使用起来也会很不方便。这些问题都是在单独模块的测试中不容易发现的。</span></p>
<p align=left><span>问：什么时候做集成测试？是不是越快越好？</span></p>
<p align=left><span>答：原则上是当一个模块稳定的时候，就可以把它集成到系统中，和整个系统一起进行测试。在模块本身稳定之前就提早做集成测试，可能会报告出很多</span><span> Bug</span><span>，但是这些由于提早测试而发现的</span><span> Bug </span><span>有点像汽车司机在等待绿灯时不耐烦而拼命地按喇叭</span><span>——</span><span>有点像噪音。我们还是要等到适当的时机再开始集成测试。</span></p>
<p align=left><span>问：但是开发人员也想早日发现并修复所有的</span><span> Bug</span><span>，软件工程的目标不就是要早发现并修正问题么？总是要等待，听起来好像没有多少效率。</span></p>
<p align=left><span>答：对，这就要提到在微软内部流行的另一种测试</span><span>——Buddy Test</span><span>伙伴测试。</span></p>
<p>&nbsp;</p>
<p align=left><strong><span>7.9&nbsp; </span></strong><strong><span>伙伴测试（</span></strong><strong><span>Buddy Test</span></strong><strong><span>）</span></strong></p>
<p align=left><span>如上所述，在开发一个复杂系统的过程中，当一个新的模块（或者旧模块的新版本）加入系统中时，往往会出现下列情况。</span></p>
<p align=left><span>（</span><span>1</span><span>）导致整个系统稳定性下降。不光影响自己的模块，更麻烦的是阻碍团队其他人员的工作。</span></p>
<p align=left><span>（</span><span>2</span><span>）产生很多</span><span> Bug</span><span>。这些</span><span> Bug</span><span>都要被输入到数据库中，经过层层会诊（</span><span>Triage</span><span>），然后交给开发人员，然后再经历一系列</span><span>Bug</span><span>的旅行，才能最后修复，这样成本变得很高。</span></p>
<p align=left><span>如何改进？一个方法当然是写好单元测试，或者运用重构技术以保证稳定性等，我们要讲的伙伴测试是指开发人员可以找一个测试人员作为伙伴（</span><span>Buddy</span><span>），在新代码签入之前，开发人员做一个私人构建（</span><span>Private Build</span><span>），其中包括了新的模块，测试人员在本地做必要的回归</span><span>/</span><span>功能</span><span>/</span><span>集成</span><span>/</span><span>探索测试，发现问题直接和开发人员沟通。通过伙伴测试把重大问题都解决了之后，开发人员再正式签入代码。</span></p>
<p align=left><span>在项目的后期，签入代码的门槛变得越来越高，大部分团队都要求</span><span>Bugfix</span><span>必须得到了伙伴测试的验证后才能签入到代码库中。</span></p>
<p>&nbsp;</p>
<p align=left><strong><span>7.10&nbsp; </span></strong><strong><span>效能测试（</span></strong><strong><span>Performance Test</span></strong><strong><span>）</span></strong></p>
<p align=left><span>用户使用软件，不光是希望软件能够提供一定的服务，而且还要求服务的质量要达到一定的水平，软件的效能是这些</span><span>&#8220;</span><span>非功能需求</span><span>&#8221;</span><span>，或者说</span><span>&#8220;</span><span>服务质量需求</span><span>&#8221;</span><span>的一部分。</span></p>
<p align=left><span>效能测试要验证的问题是：软件在设计负载内能否提供令用户满意的服务质量。这里涉及如下两个概念。</span></p>
<p align=left><strong><span>1</span></strong><strong><span>．设计负载</span></strong></p>
<p align=left><span>首先要定义什么是正常的设计负载。</span></p>
<p align=left><span>从需求说明出发，可得出系统的正常负载。例如，一个购物网站，客户认为正常负载是每分钟承受</span><span>20</span><span>次客户请求。</span></p>
<p align=left><strong><span>2</span></strong><strong><span>．令用户满意的服务质量</span></strong></p>
<p align=left><span>要定义什么样的质量是令用户满意的。</span></p>
<p align=left><span>比如，同一个购物网站，用户满意的服务质量可以定义为：每个用户的请求都能在</span><span>2</span><span>秒钟内返回结果。</span></p>
<p align=left><span>对以上两点还可以逐步细化。</span></p>
<p align=left><strong><span>1</span></strong><strong><span>．设计负载的细化</span></strong></p>
<p align=left><span>上面我们只提到</span><span>&#8220;</span><span>承受</span><span> 20 </span><span>次客户请求</span><span>&#8221;</span><span>，那么这些客户的请求到底是什么，可以按请求发生的频率来分类：</span></p>
<p align=left><span>（</span><span>1</span><span>）用户登录（</span><span>10%</span><span>）。</span></p>
<p align=left><span>（</span><span>2</span><span>）用户查看某商品详情（</span><span>50%</span><span>）。</span></p>
<p align=left><span>（</span><span>3</span><span>）用户比较两种商品（</span><span>10%</span><span>）。</span></p>
<p align=left><span>（</span><span>4</span><span>）用户查看关于商品的反馈（</span><span>20%</span><span>）。</span></p>
<p align=left><span>（</span><span>5</span><span>）用户购买商品，订单操作（</span><span>5%</span><span>）。</span></p>
<p align=left><span>（</span><span>6</span><span>）所有其他请求（</span><span>5%</span><span>）。</span></p>
<p align=left><strong><span>2</span></strong><strong><span>．服务质量的细化</span></strong></p>
<p align=left><span>有些请求，是要对数据作</span><span>&#8220;</span><span>写</span><span>&#8221;</span><span>操作，可以要求慢一些，比如</span><span>&#8220;</span><span>用户下订单，购买商品</span><span>&#8221;</span><span>，这一服务质量请求可以放宽为</span><span>5</span><span>秒钟，甚至更长。除了用户体验到的</span><span>&#8220;2</span><span>秒钟页面刷新</span><span>&#8221;</span><span>目标外，效能测试还要测试软件内部各模块的效能，这要求软件的模块能报告自身的各种效能指标，通过</span><span>Perfmon</span><span>或其他测试工具表现出来。</span></p>
<p align=left><span>和别的测试不同，效能测试对硬件要有固定的要求，而且每次测试需要在相同的机器和网络环境中进行，这样才能避免外部随机因素的干扰，得到精准的效能数据。</span></p>
<p align=left><span>问：我们以前做效能测试的时候，服务器上都没有任何负载，数据库里也没有几条记录，所以效能都很不错，可是当系统真的运行起来，就不行了。这些效能测试是自欺欺人的，对么？</span></p>
<p align=left><span>答：在做效能测试的时候，的确要避免在不现实的环境中测试，例如要避免在没有任何用户、商品记录的系统上做测试；但是也没有必要为了追求真实而过分模拟随机的环境。</span></p>
<p align=left><span>简单地说，现实的环境有如下两方面。</span></p>
<p align=left><strong><span>1</span></strong><strong><span>．现实的静态数据</span></strong></p>
<p align=left><span>比如上面提到的数据库的各种记录，如果要模拟一个实际运行的商业网站，除了一定数量的用户和商品记录外，还得模拟在运行一段时间后产生的交易记录。</span></p>
<p align=left><strong><span>2</span></strong><strong><span>．现实的动态数据</span></strong></p>
<p align=left><span>这就是负载，系统中总得有一些人在同时使用这一个系统。效能测试中要考虑到</span><span>&#8220;</span><span>负载</span><span>&#8221;</span><span>，可以分为：</span></p>
<p align=left><span>（</span><span>1</span><span>）零负载，即只有静态数据，在这种情况下测试的结果应该是稳定的，可以不断地收集数据进行回归测试；</span></p>
<p align=left><span>（</span><span>2</span><span>）加上负载，根据情况可以分负载等级进行测试。</span></p>
<p align=left><span>同时，客户会问，</span><span>&#8220;</span><span>如果我的系统慢了，怎么办，我是增加机器的数量，还是提高每个机器的处理能力？</span><span>&#8221;</span><span>这是我们要回答的问题。效能测试的结果应该成为</span><span>&#8220;</span><span>发布指南</span><span>&#8221;</span><span>的一部分，给客户发布和改进系统提供参考。</span></p>
<p align=left><span>在</span><span>VSTS</span><span>中如何进行效能测试在以后会详细讲解。</span></p>
<p align=left><span>在进行负载测试的过程中，可以得到系统效能和负载的一个对应关系，这时，就可以看到能维持系统正常功能的最大负载。</span></p>
<p align=left><span>如果负载足够大，或者过分的大，那就成了下一个测试的目标</span><span>——</span><span>压力测试。</span></p>
<p>&nbsp;</p>
<p align=left><strong><span>7.11&nbsp; </span></strong><strong><span>压力测试（</span></strong><strong><span>Stress Test</span></strong><strong><span>）</span></strong></p>
<p align=left><span>压力测试严格地说不属于效能测试。压力测试要验证的问题是：软件在超过设计负载的情况下仍能够返回正常结果，没有产生严重的副作用或崩溃。</span></p>
<p align=left><span>问：为啥不要求软件在这种情况下仍然在</span><span> 2~3 </span><span>秒钟内返回结果？</span></p>
<p align=left><span>答：因为我们做不到。</span></p>
<p align=left><span>提示：我们在这一部分要求</span><span>&#8220;</span><span>正常结果</span><span>&#8221;</span><span>，啥叫</span><span>&#8220;</span><span>正常</span><span>&#8221;</span><span>？我们也要和客户达成一致。比如，同一个购物网站，所有请求都能在网络返回</span><span>&#8220;</span><span>超时</span><span>&#8221;</span><span>错误前返回，就可以认为是</span><span>&#8220;</span><span>正常</span><span>&#8221;</span><span>。</span><span> </span><span>或者网站返回</span><span>&#8220;</span><span>系统忙，请稍候</span><span>&#8221;</span><span>，也是正常结果。但是，如果用户提交的请求一部分执行，另一部分没有执行，或者用户信息丢失，这些都是不正常的结果，应该避免。</span></p>
<p align=left><span>那我们怎样增加负载？对于网络服务软件来说，主要考虑以下两个方面。</span></p>
<p align=left><span>1</span><span>．沿着用户轴延长</span></p>
<p align=left><span>以刚才的购物网站为例，正常的负载是</span><span>20</span><span>个请求</span><span>/</span><span>分钟，如果有更多的用户登录，怎么办？那么负载就会变成</span><span>30</span><span>、</span><span>40</span><span>、</span><span>100</span><span>个请求</span><span>/</span><span>分钟，或更高。</span></p>
<p align=left><span>2</span><span>．沿着时间轴延长</span></p>
<p align=left><span>做过网络服务的同事都知道，网络的负载有时间性，负载压力波峰和波谷相差很大，那么如果每时每刻负载都处于峰值，程序会不会垮掉？这就是我们要做的第二点：沿着时间轴延长。一般要模拟</span><span> 48 </span><span>小时的高负载才能认为系统能通过测试。</span></p>
<p align=left><span>与此同时，可以减少系统可用的资源，来增加压力。</span></p>
<p align=left><span>注意，压力测试的重点是验证程序不崩溃或产生副作用。在超负载的情况下，我们的程序仍然能够正确地运行，而不会死机。在给程序加压的过程中，程序中的很多</span><span>&#8220;</span><span>小</span><span>&#8221;</span><span>问题就会被放大，暴露出来。最常见的问题是：</span></p>
<p align=left><span>◆</span>&nbsp;<span>内存</span><span>/</span><span>资源泄漏，在压力下这会导致程序可用的资源枯竭，最后崩溃；</span></p>
<p align=left><span>◆</span>&nbsp;<span>一些平时认为</span><span>&#8220;</span><span>足够好</span><span>&#8221;</span><span>的算法实现会出现问题。</span></p>
<p align=left><span>比如，</span><span>Windows Platform SDK</span><span>有一个</span><span>GetTickCount()</span><span>的函数，它返回自系统启动后所经过的毫秒数，用</span><span> DWORD </span><span>来表示。经过</span><span> 47.9 </span><span>天之后</span><span>DWORD </span><span>会溢出，</span><span>GetTickCount()</span><span>会从</span><span> 0 </span><span>开始重新计数，你的程序如果用了不同的</span><span> TickCount</span><span>来计算时间，不要假设后来的</span><span> TickCount </span><span>一定会比先前的</span><span>TickCount</span><span>大，也许系统在运行一段时间后会出现莫名其妙的错误，但是系统重启动后，又找不到原因。</span></p>
<p align=left><span>◆</span>&nbsp;<span>进程</span><span>/</span><span>线程的同步死锁问题，在压力下一些小概率事件会发生，看似完备的程序逻辑也会出现问题。</span></p>
<p align=left><span>在</span><span>VSTS</span><span>中如何进行压力测试在以后的章节中会详细讲解。</span></p>
<p>&nbsp;</p>
<p align=left><strong><span>7.12&nbsp; </span></strong><strong><span>内部</span></strong><strong><span>/</span></strong><strong><span>外部公开测试（</span></strong><strong><span>Alpha Test, Beta Test</span></strong><strong><span>）</span></strong></p>
<p align=left><span>在开发软件的过程中，开发团队希望让用户直接接触到最新版本的软件，以便从用户那里收集反馈，这时开发团队会在开发过程中让特定的用户（</span><span>Alpha/Beta</span><span>用户）使用正处于开发过程中的版本，用户会通过特定的反馈渠道（</span><span>E-mail</span><span>、</span><span>BBS</span><span>）与开发者讨论使用中发现的问题，等等。这种做法成功地让部分用户心甘情愿地替开发团队测试产品并提出反馈。</span></p>
<p align=left><span>从惯例上说，</span><span>Alpha Test</span><span>一般指在团队之外，公司内部进行的测试；</span><span>BetaTest</span><span>指把软件交给公司外部的用户进行测试，与之对应地，软件就有</span><span>Alpha</span><span>、</span><span>Beta1</span><span>、</span><span>Beta2</span><span>版本。在网络普及之前，做</span><span>Beta Test</span><span>是很花费人力物力的事情，现在由于网络的传播速度很快，与外部用户的联系渠道很畅通，很多外部用户都想先睹为快。因此最近开发团队增加了反馈的密度，不必再局限于</span><span>Alpha</span><span>或者</span><span> Beta </span><span>发布，而是不断地把一些中间版本发布出去以收集反馈，美其名曰</span><span>&#8220;</span><span>技术预览版本</span><span>&#8221;</span><span>（</span><span>Technical Preview Release</span><span>）或</span><span>&#8220;</span><span>社区预览版本</span><span>&#8221;</span><span>（</span><span>Community Preview Release</span><span>）。</span></p>
<p>&nbsp;</p>
<p align=left><strong><span>7.13&nbsp; </span></strong><strong><span>易用性测试（</span></strong><strong><span>Usability Test</span></strong><strong><span>）</span></strong></p>
<p align=left><span>小燕：作为测试人员，我们是不是也要做易用性测试</span><span>?</span></p>
<p align=left><span>答：测试人员，以及其他的团队成员都可以对软件的可用性提出意见，包括以</span><span>Bug</span><span>的形式放在</span><span>TFS</span><span>中。软件的可用性并不神秘，就是让软件更好用，让用户更有效地完成工作。</span></p>
<p align=left><span>但是我觉得</span><span>&#8220;</span><span>易用性测试</span><span>&#8221;</span><span>似乎更多地用来描述一套测试软件可用性的过程，这个过程一般不是由测试人员来主导的，而是由对软件设计及对可用性有很多研究的</span><span>&#8220;</span><span>可用性设计师</span><span>&#8221;</span><span>来实行。网络上有许多关于这方面的文章，大家可以参考。</span></p>
<p align=left><span>为了弄清软件的可用性，并了解用户的需求，移山公司的员工特地进行了一个易用性测试</span><span>——</span></p>
<p align=left><span>小飞学了很酷的</span><span>Web&#8220;WPF/</span><span>我佩服</span><span>&#8221;</span><span>界面技术，然后做了一个小游戏</span><span>——3D</span><span>挖雷。大家用了之后，都觉得不错，用鼠标单击</span><span>/</span><span>双击，左键</span><span>/</span><span>右键都可以进行各种不同的操作。于是他们迫不及待地找一个</span><span>&#8220;</span><span>典型用户</span><span>&#8221;</span><span>来做易用性测试。</span></p>
<p align=left><span>王屋村的村民，石头他爹刚好路过，他被移山公司的小伙子们拉了进来，成为第一个</span><span>&#8220;</span><span>典型用户</span><span>&#8221;</span><span>。</span></p>
<p align=left><span>大家七嘴八舌地介绍了游戏的功能，就让石头他爹试一试。石头他爹看到鼠标，说，这个怎么和俺家里的不一样？小飞说：这是光电鼠标，好用得很！</span></p>
<p align=left><span>三分钟过去了，游戏还没有开始；</span></p>
<p align=left><span>五分钟，十分钟过去了，游戏还是没有进展。</span></p>
<p align=left><span>阿超走过去看看到底怎么回事</span><span>——</span></p>
<p align=left><span>原来，石头他爹手指不灵活，在按鼠标的时候鼠标的位置会稍稍移动，导致程序无法捕捉鼠标双击事件。问题是在小飞设计的游戏中，鼠标单击、双击都可以，而且是不同的功能。</span></p>
<p align=left><span>同时，有些功能还只能够通过右键弹出菜单来执行。</span></p>
<p align=left><span>石头他爹看起来很迷惑。这时，小飞说：左键</span><span>/</span><span>右键</span><span>/</span><span>单击</span><span>/</span><span>双击都可以。</span></p>
<p align=left><span>从此之后，石头他爹对每一个操作都问：是按左键还是按右键？是按一下还是两下？</span></p>
<p align=left><span>小飞露出了</span><span>&#8220;faint&#8221;</span><span>的表情。</span></p>
<p align=left><span>半个小时后，大家送走了石头他爹，同时送他一个鼠标垫作为礼物。</span></p>
<p align=left><span>阿超：（目送石头他爹的背影）幸好</span><span>&#8230;&#8230;</span></p>
<p align=left><span>小飞：幸好啥？</span></p>
<p align=left><span>阿超：幸好你还没有介绍你那超级功能，要按住</span><span>Ctrl</span><span>键，同时拖动鼠标才能使用。否则我们还要花半个小时陪石头他爹一起学习玩这个游戏。</span></p>
<p>&nbsp;</p>
<p align=left><strong><span>7.14&nbsp; "</span></strong><strong><span>小强</span></strong><strong><span>"</span></strong><strong><span>大扫荡（</span></strong><strong><span>Bug Bash</span></strong><strong><span>）</span></strong></p>
<p align=left><span>问：我们已经讲了太多的测试了，好像微软还有一个叫</span><span>&#8220;BugBash&#8221;</span><span>的活动，是啥意思？</span></p>
<p align=left><span>答：</span><span>Bug Bash</span><span>，或者叫</span><span>Bug Hunt</span><span>，简而言之，就是大家一起来找小强的活动</span><span>——</span><span>小强大扫荡。一般是安排出一段时间（一天），所有测试人员（有时也加入其他角色）放下手里的事情，专心找某种类型的小强。然后结束时，统计并奖励找得最多和最厉害的小强的员工。</span></p>
<p align=left><span>问：这是不是可以看做是</span><span>&#8220;</span><span>全体动员</span><span>Ad hoc&#8221;?</span></p>
<p align=left><span>答：一般情况下是的，但是并不是全体人员用键盘鼠标一通乱敲乱点就可以搞定，大扫荡的内容也应该事先安排好。</span></p>
<p align=left><span>这种活动，如果运用得好，会带来这样的功效：</span></p>
<p align=left><span>◆</span>&nbsp;<span>鼓励大家做探索试的测试，开阔思路；</span></p>
<p align=left><span>◆</span>&nbsp;<span>鼓励测试队伍学习并应用新的测试方法，例如在做完</span><span>&#8220;</span><span>软件安全性测试</span><span>&#8221;</span><span>培训后，立马做一个针对</span><span>&#8220;</span><span>安全性</span><span>&#8221;</span><span>的小强大扫荡，或者为</span><span>&#8220;</span><span>全球化</span><span>/</span><span>本地化测试</span><span>&#8221;</span><span>做一个小强大扫荡也是很常见的；</span></p>
<p align=left><span>◆</span>&nbsp;<span>找到很多小强，让开发人员忙一阵子。</span></p>
<p align=left><span>当然，小强大扫荡也有一些副作用：</span></p>
<p align=left><span>◆</span>&nbsp;<span>扰乱正常的测试工作；</span></p>
<p align=left><span>◆</span>&nbsp;<span>如果过分重视奖励，会导致一些数量至上，滥竽充数的做法。</span></p>
<p align=left><span>因此，两个需要提醒的细节是：</span></p>
<p align=left><span>（</span><span>1</span><span>）一定要让</span><span>&#8220;</span><span>小强大扫荡</span><span>&#8221;</span><span>有明确的目标、明了的技术支持。</span></p>
<p align=left><span>（</span><span>2</span><span>）一定要让表现突出的人介绍经验，让别人学习。</span></p>
<p align=left><span>要记住，最好的测试，是能够防止小强的出现。</span></p>
<p>&nbsp;</p>
<p align=left><strong><span>7.15&nbsp; </span></strong><strong><span>讨论</span></strong></p>
<p align=left><st1:chsdate Year="1899" Month="12" Day="30" IsLunarDate="False" IsROCDate="False" w:st="on"><strong><span>7.15.1</span></strong></st1:chsdate><strong><span> </span></strong><strong><span>十八般兵器</span></strong></p>
<p align=left><span>阿毛：超总，我的脑袋好像装不下了！听了这么多，我感觉像是身上扛着十八般兵器，它们互相碰撞，叮叮当当。我累得半死，但是不知道什么时候，对哪一种敌人用哪一种兵器，能不能总结一下！</span></p>
<p align=left><span>阿超：好，我们用软件开发的生命周期来说明一下不同的测试在不同阶段的使用。</span></p>
<p align=left><strong><span>1</span></strong><strong><span>．远景和计划阶段</span></strong></p>
<p align=left><span>此时，测试只是处于计划阶段，我们要讨论测试计划和测试设计说明书，同时要收集用户对于软件非功能性的需求，如效能、可用性、国际化等。一些</span><span>&#8220;</span><span>小强大扫荡</span><span>&#8221;</span><span>的类型也可以在这个时候初步安排。</span></p>
<p align=left><strong><span>2</span></strong><strong><span>．开发阶段。</span></strong></p>
<p align=left><span>开发人员要写单元测试，测试人员要写</span><span>BVT</span><span>。</span></p>
<p align=left><span>对于每一个成功的构建，测试人员要运行功能测试</span><span>/</span><span>场景测试，同时建立回归测试基准以便开始回归测试。各类测试人员要进行探索式测试以求尽早发现问题。</span></p>
<p align=left><span>随着软件功能的逐步完善，测试人员要进行集成测试。这时，团队可以开展对程序非功能性特性的测试，如效能</span><span>/</span><span>压力测试、国际化</span><span>/</span><span>本地化测试、安全性测试、可用性、适用性测试等。在这个时候，可以考虑分析各个模块的代码覆盖率，以增加测试的有效性。根据计划，各种类型的</span><span>&#8220;</span><span>小强大扫荡</span><span>&#8221;</span><span>会以适当的频率发生。</span></p>
<p align=left><strong><span>3</span></strong><strong><span>．稳定阶段。</span></strong></p>
<p align=left><span>到了一个开发阶段的尾声，这时测试团队就可以依据以前制定的验收标准，对软件逐项进行验收测试。按照测试计划，各个方面的测试都会宣布</span><span>&#8220;</span><span>测试完成</span><span>&#8221;——</span><span>所有想到的测试都做了，所有问题都发现了。在此阶段，团队也可以把软件发布给外部进行</span><span>Alpha/Beta</span><span>测试。</span></p>
<p align=left><span>这时，伙伴测试会用于保证新代码签入前能得到足够的检测。</span></p>
<p align=left><span>一般情况下，测试队伍要把迄今为止发现的所有小强都重新试一遍，确保它们都在最后的版本中被清除了，没有一个</span><span>&#8220;</span><span>回归</span><span>&#8221;</span><span>出现。</span></p>
<p align=left><strong><span>4</span></strong><strong><span>．发布阶段。</span></strong></p>
<p align=left><span>测试队伍要把尽可能多的测试用例自动化，并为下一个版本的测试工作做好准备。</span></p>
<p align=left><st1:chsdate Year="1899" Month="12" Day="30" IsLunarDate="False" IsROCDate="False" w:st="on"><strong><span>7.15.2</span></strong></st1:chsdate><strong><span> </span></strong><strong><span>怎样写测试计划</span></strong></p>
<p align=left><span>这会在后面的章节中讨论。测试计划的模板在移山社区网站上有下载。</span></p>
<p align=left><st1:chsdate Year="1899" Month="12" Day="30" IsLunarDate="False" IsROCDate="False" w:st="on"><strong><span>7.15.3</span></strong></st1:chsdate><strong><span> </span></strong><strong><span>如果一个</span></strong><strong><span>Bug</span></strong><strong><span>在实际应用中根本不可能发生，这还是一个</span></strong><strong><span>Bug</span></strong><strong><span>么</span></strong></p>
<p align=left><span>看这里：</span><span><a href="http://www.testingcraft.com/Bug-in-forest.pdf">http://www.testingcraft.com/Bug-in-forest.pdf</a></span><span>。</span></p>
<p align=left><span>另外，要知道这世界上有各种各样的用户，有些用户</span><span>&#8220;</span><span>亡软件之心不死</span><span>&#8221;</span><span>，</span><span>IE </span><span>和</span><span> Windows </span><span>的许多安全漏洞，都在这些用户的努力下被发现并且被利用了。很多人当初会说</span><span>&#8220;</span><span>缓冲区溢出？这是根本不会发生的事，用户怎么会在字符串后面加这么多乱七八糟的东西？！</span><span>&#8221;</span><span>。</span></p>
<p align=left><st1:chsdate Year="1899" Month="12" Day="30" IsLunarDate="False" IsROCDate="False" w:st="on"><strong><span>7.15.4</span></strong></st1:chsdate><strong><span> Bug</span></strong><strong><span>的数量和测试人员的工作效率有关么？和工作人员的工作绩效有关么</span></strong></p>
<p align=left><span>阿亨：当然有关！我们会在以后的实践中碰到这些问题。</span></p>
<p align=left><span>阿超：有关，但是也不是太有关。一个极端的例子，如果一个开发人员写的模块没有任何</span><span>Bug</span><span>，那测试人员的工作效率如何衡量？我们以后再说。</span></p>
<p align=left><st1:chsdate Year="1899" Month="12" Day="30" IsLunarDate="False" IsROCDate="False" w:st="on"><strong><span>7.15.5</span></strong></st1:chsdate><strong><span> </span></strong><strong><span>有错不改</span></strong></p>
<p align=left><span>果冻：微软的产品经过这么多版本的不断完善，应该是把所有问题都搞定，</span><span>&#8220;</span><span>止于至善</span><span>&#8221;</span><span>了吧？</span></p>
<p align=left><span>阿超：那也不一定，在非常有名的电子表格软件</span><span>Excel</span><span>中，就有这样一个</span><span>Bug</span><span>：</span><span>Excel </span><span>的日期计算功能认为</span><span>1900</span><span>年是一个闰年，这是不对的，但是它愣是一直没有改正这个错误。</span></p>
<p align=left><span>众人：真的？为什么屡教不改呢？</span></p>
<p align=left><span>阿超：故事是这样的，当时这类电子表格软件的市场领头羊是</span><span>Lotus <st1:chsdate Year="2001" Month="2" Day="3" IsLunarDate="False" IsROCDate="False" w:st="on">1-2-3</st1:chsdate></span><span>这一款软件。它的日期计算功能有一个</span><span>Bug</span><span>，就是把</span><span>1900 </span><span>年当作闰年。这类软件在内部把日期保存为</span><span>&#8220;</span><span>从</span><st1:chsdate Year="1900" Month="1" Day="1" IsLunarDate="False" IsROCDate="False" w:st="on"><span>1900/1/1</span></st1:chsdate><span> </span><span>到当前日期的天数</span><span>&#8221;</span><span>这样的一个整数。</span><span>Excel </span><span>作为后来者，要支持</span><span> Lotus 1-2-3 </span><span>的数据文件格式，这样才能正确处理别的软件产生的格式文件。这个错误就这么延续下来了，每一版本都有人报告，但是都没有改正。我们可以在</span><span>Excel</span><span>中试试看：</span></p>
<p align=left><span>在任意格子（</span><span>cell</span><span>）中输入</span><span>&#8220;=DATE(1900,2,28)&#8221;</span><span>，并且定义这个格子的格式为数字。大家可以看到数值变为：</span><span>59</span><span>。表明</span><span> <st1:chsdate Year="1900" Month="2" Day="28" IsLunarDate="False" IsROCDate="False" w:st="on">1900/2/28</st1:chsdate> </span><span>是</span><st1:chsdate Year="1900" Month="1" Day="1" IsLunarDate="False" IsROCDate="False" w:st="on"><span>1900/1/1</span></st1:chsdate><span>开始的第</span><span>59</span><span>天。</span></p>
<p align=left><span>输入</span><span>&#8220;=DATE(1900,2,29)&#8221;</span><span>，可以看到</span><span> 60! </span><span>这是一个不存在的日期！</span></p>
<p align=left><span>输入</span><span>&#8220;=DATE(1900,3,1)&#8221;</span><span>，数值是</span><span> 61</span><span>，事实上，这应该是</span><span> 60</span><span>。从这一天开始的所有日期都错了一天。</span></p>
<p align=left><span>果冻：还是可以抓住机遇，促成飞跃，在某一个版本彻底改好，不就是一个数字嘛。</span></p>
<p align=left><span>阿超：改这个问题，技术上一点问题都没有。但是在现实中会出现下列问题：</span></p>
<p align=left><span>（</span><span>1</span><span>）几乎所有现存文件的日期数据都要减少一天，所有依赖于日期的</span><span>Excel</span><span>公式也要做检查和修改。这在现实生活中是很难办到的。</span></p>
<p align=left><span>（</span><span>2</span><span>）</span><span>Excel </span><span>的日期问题解决了，但是其他软件还是有这个</span><span> Bug</span><span>，数据文件在不同软件中使用，就会有很头痛的兼容性问题。</span></p>
<p align=left><span>总之，这个问题就这样一直留下来了。中间也有人想改过，你要注意看</span><span> Excel </span><span>的</span><span> Options </span><span>设臵，就会发现有这样一个设臵</span><span>——</span><span>使用</span><span>1904</span><span>年开始的日期计算系统</span><span> </span><span>（</span><span>use 1904 date system</span><span>）（如图</span><span>7-1</span><span>所示），但是一般的用户谁没事在这里打一个勾？<br>
<table class=ln cellSpacing=0 borderColorDark=#ffffff align=center bgColor=#ddddd borderColorLight=#999999 border=1>
    <tbody>
        <tr>
            <td bgColor=#ffffff><a href="http://new.51cto.com/files/uploadimg/20090205/161347589.jpg" target=_blank><img class=fit-image onmousewheel="javascript:return big(this)" style="WIDTH: 498px" height=134 alt="" src="http://images.51cto.com/files/uploadimg/20090205/161347589.jpg" width=598 onload="javascript:if(this.width>498)this.style.width=498;" border=0 src_cetemp="http://images.51cto.com/files/uploadimg/20090205/161347589.jpg"></a>&nbsp;<a href="http://book.51cto.com/files/uploadimg/20060921/153223104.gif" target=_blank></a></td>
        </tr>
        <tr>
            <td class=it align=middle bgColor=#dddddd>（点击查看大图）图7-1</td>
        </tr>
    </tbody>
</table>
</span></strong></p>
<img src ="http://www.cnitblog.com/stomic/aggbug/61787.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2009-10-13 10:18 <a href="http://www.cnitblog.com/stomic/archive/2009/10/13/61787.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>登陆、添加、删除、查询模块的测试点小结</title><link>http://www.cnitblog.com/stomic/archive/2009/10/08/61733.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Thu, 08 Oct 2009 13:39:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2009/10/08/61733.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/61733.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2009/10/08/61733.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/61733.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/61733.html</trackback:ping><description><![CDATA[以前在这里看到一篇<a onclick="javascript:tagshow(event, '%CE%C4%D5%C2');" href="javascript:;" target=_self><u><strong><font color=#000000>文章</font></strong></u></a>说，要积累各个常用模块的<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#000000>测试</font></strong></u></a>点，然后到需要测试的时候就根据这些测试点设计<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4%D3%C3%C0%FD');" href="javascript:;" target=_self><u><strong><font color=#000000>测试用例</font></strong></u></a>，我觉得这是一个好方法，就决定总结一下。我的实际经验不多，根据我在论坛中学到的零散的东西和自己的想象，总结出以下几点，欢迎各位继续补充。
<p>　　1、登陆　　2、添加　　3、查询　　4、删除</p>
<p>　　<strong>1、登陆</strong></p>
<p>　　①&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用户名和密码都符合要求（格式上的要求）</p>
<p>　　②&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用户名和密码都不符合要求（格式上的要求）</p>
<p>　　③&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用户名符合要求，密码不符合要求（格式上的要求）</p>
<p>　　④&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 密码符合要求，用户名不符合要求（格式上的要求）</p>
<p>　　⑤&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 用户名或密码为空</p>
<p>　　⑥&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a onclick="javascript:tagshow(event, '%CA%FD%BE%DD%BF%E2');" href="javascript:;" target=_self><u><strong><font color=#000000>数据库</font></strong></u></a>中不存在的用户名，不存在的密码</p>
<p>　　⑦&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 数据库中存在的用户名，错误的密码</p>
<p>　　⑧&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 数据库中不存在的用户名，存在的密码</p>
<p>　　⑨&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 输入的数据前存在空格</p>
<p>　　⑩&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 输入正确的用户名密码以后按[enter]是否能登陆</p>
<p>　　<strong>2、添加</strong></p>
<p>　　①&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 要添加的数据项均合理，检查数据库中是否添加了相应的数据</p>
<p>　　②&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 留出一个必填数据为空</p>
<p>　　③&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 按照边界值等价类设计测试用例的原则设计<a onclick="javascript:tagshow(event, '%C6%E4%CB%FB');" href="javascript:;" target=_self><u><strong><font color=#000000>其他</font></strong></u></a>输入项的测试用例</p>
<p>　　④&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不符合要求的地方要有错误提示</p>
<p>　　⑤&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是否支持table键</p>
<p>　　⑥&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 按enter是否能保存</p>
<p>　　⑦&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 若提示不能保存，也要察看数据库里是否多了一条数据</p>
<p>　　<strong>3、删除</strong></p>
<p>　　①&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 删除一个数据库中存在的数据，然后查看数据库中是否删除</p>
<p>　　②&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 删除一个数据库中并不存在的数据，看书否有错误提示，并且数据库中没有数据被删除</p>
<p>　　③&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 输入一个格式错误的数据，看是否有错误提示，并且数据库中没有数据被删除。</p>
<p>　　④&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 输入的正确数据前加空格，看是否能正确删除数据</p>
<p>　　⑤&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 什么也不输入</p>
<p>　　⑥&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是否指出table键</p>
<p>　　⑦&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是否支持enter键</p>
<p>　　<strong>4、查询</strong></p>
<p>　　精确查询：</p>
<p>　　①&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 输入的查询条件为数据库中存在的数据，看是否能正确地查出相应得数据</p>
<p>　　②&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 输入正确的查询条件以前加上空格，看是否能正确地查出相应的数据</p>
<p>　　③&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 输入格式或范围不符合要求的数据，看是否有错误提示</p>
<p>　　④&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 输入数据库中不存在的数据</p>
<p>　　⑤&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不输入任何数据</p>
<p>　　⑥&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是否支持table键</p>
<p>　　⑦&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 是否支持enter键</p>
<p>　　模糊查询：</p>
<p>　　在精确查询的基础上加上以下一点：</p>
<p>　　①&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 输入一些字符，看是否能查出数据库中所有的相关信息<br><strong>用户注册和密码修改测试点</strong></p>
<p>　　一．用户注册</p>
<p>　　只从用户名和密码角度写了几个要考虑的测试点，如果需求中明确规定了安全问题，Email，出生日期，地址，性别等等一系列的格式和字符要求，那就都要写用例测了~</p>
<p>　　以等价类划分和边界值法来分析</p>
<p>　　1、填写符合要求的数据注册： 用户名字和密码都为最大长度 （边界值分析，取上点）</p>
<p>　　2、填写符合要求的数据注册 ：用户名字和密码都为最小长度 （边界值分析，取上点）</p>
<p>　　3、填写符合要求的数据注册：用户名字和密码都是非最大和最小长度的数据（边界值分析，取内点）</p>
<p>　　4、必填项分别为空注册</p>
<p>　　5、用户名长度大于要求注册1位（边界值分析，取离点）</p>
<p>　　6、用户名长度小于要求注册1位（边界值分析，取离点）</p>
<p>　　7、密码长度大于要求注册1位（边界值分析，取离点）</p>
<p>　　8、密码长度小于要求注册1位（边界值分析，取离点）</p>
<p>　　9、用户名是不符合要求的字符注册（这个可以划分几个无效的等价类，一般写一两个就行了，如含有空格，#等，看需求是否允许吧~）</p>
<p>　　10、密码是不符合要求的字符注册（这个可以划分几个无效的等价类，一般写一两个就行了）</p>
<p>　　11、两次输入密码不一致（如果注册时候要输入两次密码，那么这个是必须的）</p>
<p>　　12、重新注册存在的用户</p>
<p>　　13、改变存在的用户的用户名和密码的大小写，来注册。（有的需求是区分大小写，有的不区分）</p>
<p>　　14、看是否支持tap和enter键等；密码是否可以复制粘贴；密码是否以* 之类的加秘符号显示</p>
<p>　　二．修改密码</p>
<p>　　当然具体情况具体分析哈~不能一概而论~</p>
<p>　　实际测试中可能只用到其中几条而已，比如银行卡密码的修改，就不用考虑英文和非法字符，更不用考虑那些ＴＡＰ之类的快捷键．</p>
<p>　　而有的需要根据需求具体分析了，比如连续出错多少次出现的提示，和一些软件修改密码要求一定时间内有一定的修改次数限制等等。</p>
<p>　　1、不输入旧密码，直接改密码</p>
<p>　　2、输入错误旧密码</p>
<p>　　3、不输入确认新密码</p>
<p>　　4、不输入新密码</p>
<p>　　5、新密码和确认新密码不一致</p>
<p>　　6、新密码中有空格</p>
<p>　　7、新密码为空</p>
<p>　　8、新密码为符合要求的最多字符</p>
<p>　　9、新密码为符合要求的最少字符</p>
<p>　　10、新密码为符合要求的非最多和最少字符</p>
<p>　　11、新密码为最多字符-1</p>
<p>　　12、新密码为最少字符+1</p>
<p>　　13、新密码为最多字符+1</p>
<p>　　14、新密码为最少字符-1</p>
<p>　　15、新密码为非允许字符（如有的密码要求必须是英文和数字组成，那么要试汉字和符号等）</p>
<p>　　16、看是否支持tap和enter键等；密码是否可以复制粘贴；密码是否以* 之类的加秘符号</p>
<p>　　17、看密码是否区分大小写，新密码中英文小写，确认密码中英文大写．</p>
<p>　　18、新密码与旧密码一样能否修改成功．</p>
<p>　　有个朋友问我，注册的时候测试了密码长度，修改的时候为什么还要测试。</p>
<p>　　我在这里举个我亲身经历的例子，以前我玩一个游戏，叫恋爱盒子，在游戏里我把密码改成了xuewufengtian，后来怎么也上不去了。因为资料填写不全无法找回密码。后来我在一次注册过程中发现，注册的时候密码长度最长是10位，这时我灵机一动，用了原来的用户名和xuewufengt的密码就进去了。 这表明，修改密码时候的最大长度和注册及登陆的时候密码最大长度有可能是不一致的。</p>
<p><br></p>
<img src ="http://www.cnitblog.com/stomic/aggbug/61733.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2009-10-08 21:39 <a href="http://www.cnitblog.com/stomic/archive/2009/10/08/61733.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>软件测试人员容易忽略的问题</title><link>http://www.cnitblog.com/stomic/archive/2009/09/11/61373.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 11 Sep 2009 10:06:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2009/09/11/61373.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/61373.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2009/09/11/61373.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/61373.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/61373.html</trackback:ping><description><![CDATA[<span><font size=3><strong>通常</strong><a onclick="javascript:tagshow(event, '%C8%ED%BC%FE%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#000000>软件测试</font></strong></u></a><strong>会暴露软件中的缺陷，经过修正后可以保证软件系统的功能满足需求并正确运行。但是，在</strong><a onclick="javascript:tagshow(event, '%CF%B5%CD%B3%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#000000>系统测试</font></strong></u></a><strong>和确认测试中，测试人员容易遗漏一些隐藏的缺陷。众所周知，软件测试不可能发现所有的缺陷，而软件开发周期各个阶段仍然存在注入缺陷的可能，但是，有一些缺陷是测试中容易忽略的，也就是说，通过测试方法和用例可以充分暴露这些缺陷，遗憾的是，它们往往被忽略或者某种原因忘记测试了，这就给软件留下了隐患或者危机。这些容易被忽略的缺陷包括：</strong></font><br><br></span><font size=3><span><strong>　　1、安装缺陷</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　通常项目组完成代码后，发布时候安装打包是最后一个环节，而软件测试人员通常在测试的时候，没有仔细的测试这一部分，而把用例集中在</strong><a onclick="javascript:tagshow(event, '%C6%E4%CB%FB');" href="javascript:;" target=_self><u><strong><font color=#000000>其他</font></strong></u></a><strong>功能上。安装时候的缺陷通常通过拷贝而不是运行安装程序方式给测试人员安装软件，结果正式安装时候出现问题，引起例如控件没有注册，注册表没有导入等。删除时候没有注意安装文件夹是否存在用户文件，造成数据丢失;使用绝对路径;安装顺序没有说明书。</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　2、配置文件</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　有些文件在ini等配置文件中写出了管理员口令密码等信息，而且是明文的!这是一个安全隐患。另外，有些安装文件的XML文件，为了方便在</strong><a onclick="javascript:tagshow(event, '%CA%FD%BE%DD%BF%E2');" href="javascript:;" target=_self><u><strong><font color=#000000>数据库</font></strong></u></a><strong>和中间层连接文件中写入了Admin口令和密码。作为一个合格的软件测试人员，必须检查这些可以用记事本打开的文件。因为，一个稍有常识而且喜欢探索的用户，可能从中获取信息而成为不自觉的黑客。所以，配置文件可能成为软件安全方面的一个缺陷。</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　3、网页安全缺陷</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　现在网站开发已经注意到：登陆网站进入其内部网页后，直接拷贝网址，然后粘贴到另一IE窗口输入，可以绕过登陆直接访问。也许商业网站很关注这个问题，但是很多行业软件却很容易忽略。</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　网页安全缺陷还可能存在于IE弹出的子窗口。有些设计不严格的软件，在主页面关闭的时候子页面还可以运行，这是一个明显的漏洞，而且还大大增加了错误发生的几率。</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　4、判断顺序/逻辑缺陷</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　对界面进行多个输入判断的时候，非常容易出现这种问题。例如判断年月顺序，判断长度，判断非空等。假如操作员仅仅满足单个条件，保存不能成功;而按界面从上之下顺序一一满足条件之后，保存是没有问题的。但是，改变一下输入的次序，校验失效。例如，一一满足条件之后，不保存，倒过来将上面的输入改成非法输入，然后保存，结果居然也能成功，这是因为原先的判断由于发生过，或者根据语句顺序只检查最后一个判断，所以没有报错。这种错误尤其在Java scrīpt脚本的页面中要注意。能够保存不能保证数据正确，有可能引起系统崩溃或者后续数据错误。所以，在测试的时候，不要按照正常的顺序输入，而是要打乱步骤，看看代码是否强健，是否在判断逻辑上没有错误。良好的代码应该经得起折腾，至少保存时会再此全部进行判断，而不只是简简单单走到判断的最后一行。</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　5、调试语句和冗余信息</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　维护项目和升级改造的推广系统最容易潜伏这类缺陷。典型表现在没有删除或者屏蔽调试语句。弹出一个界面不友好的提示信息，会使不明真相的用户产生误以为系统发生了严重故障，从而引起对软件的不信任感。页面中某个角落存在当前客户不需要的冗余按钮和功能也是一种缺陷。多余的功能会使用户以为是额外附加部分而去使用，其结果可想而知;而多余的按钮会误导好奇心强的用户操作，产生不必要的错误。</strong></span></font><span><br><br><font size=3><strong>　　同样值得关注的还有参数设置，由于没有实际数据，开发人员在调试或者</strong><a onclick="javascript:tagshow(event, '%B5%A5%D4%AA%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#000000>单元测试</font></strong></u></a><strong>的时候，习惯性的进行自我设定而忘了删除，软件测试人员可能会忽略掉了这部分测试，也可能导致在客户现场发生错误而影响系统发布和验收。</strong></font><br><br></span><font size=3><span><strong>　　6、不可重现的故障</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　新参加软件测试的人员或者新来的开发人员总是要问，不可重现的缺陷是否需要记录，有必要吗?回答是肯定的。测试必须如实的记录发生的问题，也许不能重现，或者使非软件系统本身问题，但是，可能这些偶然性背后是有规律的，不记录这些，就不可能发现这些规律。</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　7、多节点的逆向流转缺陷</strong></span></font><span><br><br><font size=3><strong>　　当前软件不少喜欢使用</strong><a onclick="javascript:tagshow(event, '%B9%A4%D7%F7');" href="javascript:;" target=_self><u><strong><font color=#000000>工作</font></strong></u></a><strong>流来驱动。工作流的问题，就是可能出现多个流向分支。测试容易忽略的部分，就是工作流多节点的逆向流转。例如，通过不通过涉及两个分支，但是流程逆转的时候，有可能不是回到上一节点而是平级的另一个节点去了。软件测试要格外注意这类用例的设计。另外，有些时候默认分支在向前的时候是有默认值的，例如默认通过，那么保存的时候要提示用户是否通过，否则可能由于操作疲劳而走错了节点，引起回退。</strong></font><br><br></span><font size=3><span><strong>　　8、输入框缺陷</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　试过往输入框粘贴数据而不是直接输入吗?可能这里会出现问题。按Ctrl+V的时候，输入框会根据长度大小自动截断输入长度。但是用鼠标，截断可能会失效。有一次测试人员就是用这种方法把一篇Word文档输入进去了，保存的时候，数据库崩溃。有些网站登陆的口令****可以拷贝下来的，只要放在剪贴板里面马上明文显示。</strong></span></font><span><br><br><strong><font size=3>　　输入框可以说是问题最多的部分，能够引起的麻烦也很多。日期、数字、文本等等，都需要耐心的测试一下。</font><br><br></strong></span><font size=3><span><strong>　　9、界面布局缺陷</strong></span></font><span><br><br><strong><font size=3>　　曾经有一次，项目经理回来向测试部反映一个问题，客户对界面不满意。原因很简单，因为界面上删除按钮和保存按钮挨得很近。结果有些操作不熟练的业务人员，很容易误按。这个问题是测试人员没有意料到的，因此注意关闭、删除、退出按钮与保存、下一步等按钮的距离。类似的按钮应按此规则排列分布。</font><br><br></strong></span><font size=3><span><strong>　　界面布局还可能发生在窗口最大化和最小化上，有可能窗口缩小的时候没有下拉框或不匹配分辨率，对用户来讲，这个错误实在很低级。有些用户由于操作习惯，非常不喜欢腾出手使用鼠标，尤其是大量输入的界面，因此，要注意设置键盘的快捷方式。还有，按Tab定位到下一焦点时要注意顺序，避免跳转太灵活而让操作人员感到无从适应，在界面进行维护或者修改的时候，不要忘了软件测试开发人员是否无意改变了这些快捷方式和跳转顺序。</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　10、版本和补丁包的环境问题</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　理论上讲，这属于兼容性测试应该覆盖的问题。有些客户很喜欢更新最新的软件版本或者</strong><a onclick="javascript:tagshow(event, '%CE%A2%C8%ED');" href="javascript:;" target=_self><u><strong><font color=#000000>微软</font></strong></u></a><strong>时不时打些补丁包，问题就出现了。有时候升级不一定是好事。这些问题最好在测试的时候增加几个用例，多用不同软件版本的机器跑一跑。软件测试有个定律是：你没跑过的地方，就一定会出事。经常听到开发人员抱怨，怎么我的机器没问题，你的机器就有事了呢?这不能完全靠</strong><a onclick="javascript:tagshow(event, '%C5%E4%D6%C3%B9%DC%C0%ED');" href="javascript:;" target=_self><u><strong><font color=#000000>配置管理</font></strong></u></a><strong>员解决问题，环境配置项是大家最容易忽略的。</strong></span></font><span lang=EN-US style="COLOR: black; FONT-FAMILY: Tahoma; mso-bidi-font-size: 10.5pt"><br><br></span><font size=3><span><strong>　　11、用户管理缺陷</strong></span></font><span><br><br><strong><font size=3>　　用户管理的角色和授权需要好好研究一下，作过测试的人员都知道，有时候为了测试的方便，测试用户都是具有超级权限的用户。而且，比较容易忽略用户管理这一部分的测试。往往发往客户的时候，很多测试用户都没有删除。</font><br><br><font size=3>　　另外，有些接口的用户和口令，到软件使用寿命结束都没有更改过。在一次测试中，软件测试人员发现，给一个用户授超级用户权限，之后更改这个用户为受限权限。使用中发现，用户居然没有真正回收权限，用户管理界面上没有任何不对。及早准备用户管理用例，不要等到测试快结束时候才想起。</font><br><br></strong></span><font size=3><span><strong>　　12、常识缺陷</strong></span></font><span><br><br><strong><font size=3>　　从逻辑或者统计学上讲，计算机是允许如此处理的，但是从常识上来讲，这些情况不可能发生。例如电话号码不可能出现小数点，终止时间不能大于开始时间等等。除此之外，常识还要结合业务特点来进行判断，因此，开发和测试人员要格外注意对自己知识的培养以及增加对需求细节的了解。不能因为一味追求进度而采用最简单的代码来实现，对用户来说，这些错误可能是很荒谬的。</font><br><br><font size=3>　　尽管我们不可能完美的测试一个软件，但是我们仍然可以改进我们的软件测试。每次测试结束，及时总结测试中的不足，进一步完善用例。思考一下那些容易忽略的软件缺陷，能提高对软件测试的认识，提高所在组织软件的质量。</font></strong></span>
<img src ="http://www.cnitblog.com/stomic/aggbug/61373.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2009-09-11 18:06 <a href="http://www.cnitblog.com/stomic/archive/2009/09/11/61373.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>编写Bug，Report Bug的注意事项</title><link>http://www.cnitblog.com/stomic/archive/2009/09/03/61211.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Thu, 03 Sep 2009 02:10:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2009/09/03/61211.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/61211.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2009/09/03/61211.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/61211.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/61211.html</trackback:ping><description><![CDATA[<strong>　1.Bug的Description的描述</strong>
<p>　　Report <a onclick="javascript:tagshow(event, 'Bug');" href="javascript:;" target=_self><u><strong><font color=#0000ff>Bug</font></strong></u></a>时，描述有效的Description的关键点：</p>
<p>　　Condense－精简，清晰而简短；</p>
<p>　　Accurate－准确，确定是Bug；</p>
<p>　　Neutralize－用中性的语言描述事实，不带偏见，不用幽默或者情绪化的语言；</p>
<p>　　Precise－精确；</p>
<p>　　Isolate－定位，尽量缩小这个问题的范围；</p>
<p>　　Generalize－还有没有<a onclick="javascript:tagshow(event, '%C6%E4%CB%FB');" href="javascript:;" target=_self><u><strong><font color=#0000ff>其他</font></strong></u></a>的某些地方存在这样的问题；</p>
<p>　　Re-Create－如何引发和重现这个Bug？（环境，步骤，前提条件）</p>
<p>　　Impact－影响，这个缺陷对客户的影响以及对<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#0000ff>测试</font></strong></u></a>的影响；</p>
<p>　　DeBug－怎么做才可以让开发更容易来修改这个Bug？（跟踪，截图，<a onclick="javascript:tagshow(event, '%C8%D5%D6%BE');" href="javascript:;" target=_self><u><strong><font color=#0000ff>日志</font></strong></u></a>，直接访问等等）</p>
<p>　　Evidence－证据。</p>
<p><strong>　　Condense－精简，清晰而简短</strong></p>
<p>　　首先，去掉不必要的词；</p>
<p>　　其次，不要添加无关的信息。</p>
<p>　　包含相应的信息是最重要的，但是确保这些信息都是有用的。对于那些没有描述清楚如何重现或者难以理解的问题，都应该提供更多的信息。但也要避免写过多的不必要的信息。<a href="http://www.51testing.com/batch.download.php?aid=15348" target=_blank></a></p>
<p><strong>　　Accurate－准确，确定是Bug</strong></p>
<p>　　确信是一个Bug，避免因为其他原因，导致错误的Report Bug，需要考虑：</p>
<ul>
    <li>是否会因为安装的某个原因导致这个问题？例如，是否安装了正确的版本而且各种先决条件也已经满足？是否登陆，安全设定，命令或者操作的顺序有错误？
    <li>是否存在清除不干净，或者结果不完整，或者因为上次测试的某些更改导致？
    <li>是否是网络或者环境的问题？
    <li>是否理解了期望的结果？
    <li>中性的语言
    <li>客观的描述Bug，不要使用幽默的或者其他带有感情色彩的语句。在提交Bug之前，仔细阅读Bug的描述，删除或者修改可能让人产生歧义的句子。</li>
</ul>
<p align=center><a href="http://www.51testing.com/batch.download.php?aid=15349" target=_blank></a></p>
<p>　<strong>　Precise－精确</strong></p>
<p>　　当Bug的描述很长时，例如：&#8220;我按了回车键，然后现象A出现，接着按了后退键，现象B出现，接着输入命令&#8216;XYZ&#8217;，现象C出现&#8221;，看到这样的说明，很难明白到底想说明什么问题，三个现象中哪一个是错误的。清晰准确并且客观的描述Bug，而不是简单说明发生了什么。</p>
<p align=center><a href="http://www.51testing.com/batch.download.php?aid=15347" target=_blank></a></p>
<p>　　Isolate－定位，尽量缩小这个问题的范围</p>
<p>　　定位发现的问题。在试图隔离一个问题的时候，需要考虑下面的几点：</p>
<ul>
    <li>尝试找到最短，最简单的步骤来重现这个问题。
    <li>查看是否是外部的什么特殊的原因引起的这个问题？例如，系统挂起或延时，会不会是因为网络的问题？
    <li>对于一个存在多种输入条件的项目，尝试不断的改变输入值，并查看结果，直到确定哪个值导致的错误。
    <li>在问题描述中，在尽可能的范围内，精确描述所使用的测试输入值。例如，如果在测试中发现打印一份脚本的时候会出错，首先判断是不是打印所有的这种类型的脚本都会出错。</li>
</ul>
<p>　<strong>归纳</strong></p>
<p>　　Report Bug时，采用合理的步骤来确定这个问题是通常会发生还是偶然一次出现或者是在特殊条件才出现。</p>
<p align=center><a href="http://www.51testing.com/batch.download.php?aid=15350" target=_blank></a></p>
<p>　　<strong>重现</strong></p>
<p>　　如果测试时，可以重现Bug，那么，应该准确的解释重现Bug所必需的条件。列出所有的步骤，包括精确的组合，文件名以及碰到或者重现这个问题的操作顺序。如果确认这个问题在任何文件，任何的操作顺序等条件下都会发生，那也最好能够给出一个明确的示例用来帮助开发来重现。</p>
<p>　　如果测试时，不能重现Bug，那就提供尽可能多的有效的信息。在开发没有重现或者开发没有解决之前，不要清除相应的测试数据，或者至少要备份这些数据。</p>
<p>　　<strong>影响</strong></p>
<p>　　发布产品时，需要判断未解决的影响问题。例如，在某一个窗口发现一个排版错误或者拼写错误，这类Bug对测试人员来说可能是微不足道的，但对于客户来说，这是接触产品的第一件事，所以必须在给客户实施前修改好。</p>
<p>　　<strong>调试</strong></p>
<p>　　如果需要，在Report时，提供跟踪、截图、日志等对捕获这个Bug有帮助的信息。</p>
<p>　<strong>　证据</strong></p>
<p>　　提供Report的是一个Bug的证据信息，这些信息可能包括操作指导，文档，必备条件等等，还有可能是客户以前反馈过来的零碎的信息，或者是竞争对手的软件中的一些标准，又或者来源于以往版本中的结果。</p>
<p>　　<strong>2.Bug的标题</strong></p>
<p>　　Bug的标题在很多情况下是一个有力的和项目组成员之间的沟通工具，在很多情形下，PM，Team Leader等只是查看Bug的标题。</p>
<ul>
    <li>简单，明确的说明问题（不能只是说出现问题）
    <li>建议（如果长度允许的话）：
    <li>使用有意义的单词；
    <li>描述环境和影响；
    <li>回答5W1H的问题（why，when，who，where，what，how）；
    <li>使用简写，例如挂起，异常中止，拼写错误等
    <li>相对于描述清楚而言，语法不是很重要
    <li>例如：下面的标题就没有提供足够多的信息。</li>
</ul>
<p>　　例一：Summary：在保存和恢复数据成员时出错。</p>
<p>　　例二：Summary：一个比较好的标题可能是这样：在WINNT环境下，XYZ的保存和恢复数据失败，数据丢失。</p>
<p>　<strong>　3.&nbsp;其它注意事项</strong></p>
<p>　　使用Bugzilla，报Bug时，需要注意以下事项：</p>
<p>　　第一，应先确认Bugzilla上已经建立了相应当前的版本；</p>
<p>　　第二，在报Bug时，需要选择，Show Advanced Fields，这样才会罗列出详细的信息，如要CC的人，QA Contact等等；</p>
<p>　　第三，Attachment，保存和发送的图片格式一般为JPG格式，OS操作系统也要选择好。</p>
<p>　　<strong>Bugzilla上，有7个严重程度等级。</strong></p>
<p>　　具体定义如下，This field describes the impact of a Bug.</p>
<ol>
    <li>blocker&nbsp;&nbsp;&nbsp; Blocks development and/or Testing work
    <li>critical&nbsp;&nbsp;&nbsp; crashes， loss of data， severe memory leak
    <li>major&nbsp;&nbsp;&nbsp; major loss of function
    <li>normal&nbsp;&nbsp;&nbsp; regular issue， some loss of functionality under specific circumstances
    <li>minor&nbsp;&nbsp;&nbsp; minor loss of function， or other problem where easy workaround is present
    <li>trivial&nbsp;&nbsp;&nbsp; cosmetic problem like misspelled words or misaligned text
    <li>enhancement&nbsp;&nbsp;&nbsp; Request for enhancement</li>
</ol>
<p>　　第四，在报告Bug时，除了在描述中说明Bug的复现步骤外，还要在Description中，添加该Bug的测试发生率。测试发生率为按照特定步骤执行多次的Bug重现率。测试发生率=ug重现次数/按照特定步骤执行的总次数。其中：对于概率性问题，执行的总次数应根据Bug的复杂程度执行（20-50次）。这样对于再现Bug，定位问题等都有帮助。</p>
<br><br>
<img src ="http://www.cnitblog.com/stomic/aggbug/61211.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2009-09-03 10:10 <a href="http://www.cnitblog.com/stomic/archive/2009/09/03/61211.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用门的概念理解响应时间和吞吐量之间的关系</title><link>http://www.cnitblog.com/stomic/archive/2008/12/09/52367.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Tue, 09 Dec 2008 02:11:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/12/09/52367.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/52367.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/12/09/52367.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/52367.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/52367.html</trackback:ping><description><![CDATA[　<a  href="http://www.51testing.com/javascr%C4%ABpt:;" ōnclick="javascrīpt:tagshow(event, '%d0%d4%c4%dc%b2%e2%ca%d4');" target="_self"><u><strong>性能测试</strong></u></a>的目的是检查软件的平均响应时间或者吞吐量是否符合指定的标准。
<p>　　例如，当测试前已经获知在线人数为10000，可以设定性能测试的目的是检测软件典型交易的平均响应时间是否符合小于5秒的指标值。</p>
<p>　　例如，当测试前不知道在线人数是多少，但是已经获知该软件在一定的时间周期内（t）必须处理N笔交易，可以设定性能测试的目的是检测软件典型交易的吞吐量是否符合大于25笔交易/秒的指标值。</p>
<p>　　但是，在第二种情况出现时，还应该考虑若软件的吞吐量符合指定的指标值时，软件典型交易的平均响应时间是否符合小于5秒的指标值。</p>
<p>　　为什么呢？</p>
<p>　　我们可以利用&#8220;门&#8221;的概念来理解这里面的偏差！</p>
<p>　　首先，我们假设如下的情况：</p>
<p>　　共有5个人；</p>
<p>　　有1扇门；</p>
<p>　　一<a  href="http://www.51testing.com/javascr%C4%ABpt:;" ōnclick="javascrīpt:tagshow(event, '%b8%f6%c8%cb');" target="_self"><u><strong>个人</strong></u></a>通过这扇门需要花费1秒的时间；</p>
<p>　　此时，这扇门的吞吐量为1人/秒。5个人通过这扇门的平均响应时间为（1+2+3+4+5）/5=3秒。</p>
<p>　　如何才能提高人的通过效率呢？即，如何才能提高门的吞吐量呢？</p>
<p>　　有两种方法：</p>
<p>　　（1）减小通过门的时间；</p>
<p>　　（2）增加门的数量</p>
<p>　　例如，</p>
<p>　　（1）将一个人通过门的时间减小为0.5秒，门的吞吐量变成了2人/秒；</p>
<p>　　（2）增加一个门，门的吞吐量也变成了2人/秒</p>
<p>　　结果是：</p>
<p>　　（1）5个人通过改善通过时间的门的平均响应时间为（0.5+1+1.5+2+2.5）/5＝1.5秒；</p>
<p>　　（2）5个人通过两扇门的平均响应时间为（1+1+2+2+3）/5＝1.8秒</p>
<p>　　此时，你可以发现，软件开发员改进软件处理并发交易请求的方法有两个，第一种是提高单个请求的处理速率，第二种是增加处理请求的线程的数量；或
者是两种方法的组合。但是，不同方法的使用并不代表吞吐量得到了提高，而同时软件典型交易的平均响应时间也获得了相同值的改善。</p>
<p>　　因此，在性能测试以吞吐量为检测指标的时候，不光要评估吞吐量是否符合了性能指标的要求，同时也必须考虑响应时间是否符合性能指标的要求。</p>
<p>　　假设，在测试前，规定了吞吐量为大于25笔交易/秒，平均响应时间为小于5秒，在测试后，若实际吞吐量等于27笔交易/秒，不能仅凭这个27笔交易/秒就确定该软件的性能符合要求了，还要看平均响应时间是否符合要求。这时的平均响应时间可能大于5秒。</p>
而，如果测试前，规定了在线人数为10000，平均响应时间为小于5秒，<img src ="http://www.cnitblog.com/stomic/aggbug/52367.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-12-09 10:11 <a href="http://www.cnitblog.com/stomic/archive/2008/12/09/52367.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）伟大骡子的一生和性能测试</title><link>http://www.cnitblog.com/stomic/archive/2008/12/09/52365.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Tue, 09 Dec 2008 02:00:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/12/09/52365.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/52365.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/12/09/52365.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/52365.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/52365.html</trackback:ping><description><![CDATA[<p><span style="font-size: 10.5pt; font-family: 宋体;">经常上课时，大家就<a  href="javascript:;" onclick="javascript:tagshow(event, '%d0%d4%c4%dc%b2%e2%ca%d4');" target="_self"><u><strong>性能测试</strong></u></a>、压力测试和容量测试分不清楚，忽然想到用讲故事的方法来进行区别，可能难免有牵强之处，拿出来给大家加深一下印象。</span></p>
<p><span style="font-size: 10.5pt; font-family: 宋体;">有一个农夫决定买一匹骡子，他认为这个骡子至少得能扛动<span lang="EN-US">3</span>袋大米，他才会决定买这匹骡子（这相当于用户提出的<font color="#ff0000">性能需求</font>）。结果他来到农贸集市上，试了好几头骡子，都不合适，最后终于有一头骡子能够比较轻松的扛动这<span lang="EN-US">3</span>袋大米，而且还潇洒的走了几步（这相当于于<font color="#ff0000">性能测试通过</font>）。然后农夫高高兴兴地牵着这头骡子回家，而且给它扛了<span lang="EN-US">4</span>袋大米（相当于让<font color="#ff0000">系统超负荷运行</font>），因为他跑了太远才买到了这匹不可多得的骡子，他想看看它到底能有多强，所以农夫决定，让这匹骡子就扛着这四袋大米走回家试试看，这匹骡子真的很厉害，刚开始的时候还一颠一跑的，可是后来实在路太远了，骡子越驮越费劲（在超负荷情况下检验系统能正常运行多久，这相当于<font color="#ff0000">压力测试</font>），
快到家的时候，已经是走两步歇一步了。终于到家了，农夫非常自豪地叫出自己的老婆，说：&#8220;老婆子，快来看看，看我买到了一头多么厉害的骡子啊！&#8221;，老婆出
来后，农夫把他和骡子在一路上的经历都告诉了老太婆，谁知这个老太婆却说：&#8220;你真蠢，这么大老远的路，也不让骡子驮着你，竟然和这头傻骡子一样走回
来！&#8221;，农夫听了，觉得非常后悔，说：&#8220;那好吧，既然在路上它没有驮我，那就让它现在补上，也算是对我的补偿。&#8221;，骡子还没有反应过来，就看那老农夫一个
箭步，跳到了骡子背上（这相当于<font color="#ff0000">容量测试</font>的极限点），可怜的骡子，无论如何也不会想到，这狠心的农夫竟然在它走了这么久之后，不但没有帮它卸掉身上的重担，更没有给它喝口水，竟然变本加厉的跳到了它那本已弯曲的背上。可怜的骡子啊，就这么一命呜乎了！就看见那个骡子、农夫和<span lang="EN-US">4</span>袋麦子一起轰然倒地。（相当于已经到了系统的最大拐点，造成了系统瘫痪，无法使用，容量测试结束）。</span></p><img src ="http://www.cnitblog.com/stomic/aggbug/52365.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-12-09 10:00 <a href="http://www.cnitblog.com/stomic/archive/2008/12/09/52365.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试用例设计白皮书－－测试用例设计综合策略</title><link>http://www.cnitblog.com/stomic/archive/2008/11/21/51762.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 21 Nov 2008 03:34:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/11/21/51762.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/51762.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/11/21/51762.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/51762.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/51762.html</trackback:ping><description><![CDATA[<strong>1. Myers提出了使用各种</strong><a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target=_self><u><font color=#202859><strong>测试</strong></font></u></a><strong>方法的综合策略：</strong>
<p>　　1) 在任何情况下都必须使用边界值分析方法，经验表明用这种方法设计出<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4%D3%C3%C0%FD');" href="javascript:;" target=_self><u><strong><font color=#202859>测试用例</font></strong></u></a>发现程序错误的能力最强。 </p>
<p>　　2) 必要时用等价类划分方法补充一些测试用例。</p>
<p>　　3) 用错误推测法再追加一些测试用例。</p>
<p>　　4) 对照程序逻辑，检查已设计出的测试用例的逻辑覆盖程度，如果没有达到要求的覆盖标准，应当再补充足够的测试用例。</p>
<p>　　5) 如果程序的功能说明中含有输入条件的组合情况，则一开始就可选用因果图法。</p>
<p>　<strong>　2.测试用例的设计步骤</strong> </p>
<p>　　1) 构造根据设计规格得出的基本<a onclick="javascript:tagshow(event, '%B9%A6%C4%DC%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>功能测试</font></strong></u></a>用例；</p>
<p>　　2) 边界值测试用例；</p>
<p>　　3) 状态转换测试用例；</p>
<p>　　4) 错误猜测测试用例；</p>
<p>　　5) 异常测试用例； </p>
<p>　　6) <a onclick="javascript:tagshow(event, '%D0%D4%C4%DC%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>性能测试</font></strong></u></a>用例；</p>
<p>　　7) 压力测试用例。</p>
<p>　<strong>　3.优化测试用例的方法</strong></p>
<p>　　1) 利用设计测试用例的8种方法不断的对测试用例进行分解与合并；</p>
<p>　　2) 采用遗传算法理论进化测试用例；</p>
<p>　　3) 在测试时利用发散思维构造测试用例</p>
<img src ="http://www.cnitblog.com/stomic/aggbug/51762.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-11-21 11:34 <a href="http://www.cnitblog.com/stomic/archive/2008/11/21/51762.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试用例设计白皮书－－测试用例基本概念</title><link>http://www.cnitblog.com/stomic/archive/2008/11/21/51761.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 21 Nov 2008 03:33:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/11/21/51761.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/51761.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/11/21/51761.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/51761.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/51761.html</trackback:ping><description><![CDATA[目 录
<p>　　1. 概述</p>
<p>　　2. <a onclick="javascript:tagshow(event, '%B2%E2%CA%D4%D3%C3%C0%FD');" href="javascript:;" target=_self><u><strong><a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>测试</font></strong></u></a>用例</strong></u></a>基本概念</p>
<p>　　2.1. 测试用例的定义</p>
<p>　　2.2. 测试用例的特征</p>
<p>　　2.3. 测试用例组成元素</p>
<p>　　2.4. 测试用例设计原则</p>
<p>　　3. 测试用例设计方法</p>
<p>　　3.1. 等价类划分方法</p>
<p>　　3.2. 边界值分析方法</p>
<p>　　3.3. 错误推测方法</p>
<p>　　3.4. 因果图方法</p>
<p>　　3.5. 判定表驱动分析方法</p>
<p>　　3.6. 正交实验设计方法</p>
<p>　　3.7. 功能图分析方法</p>
<p>　　3.8. 场景设计方发</p>
<p>　　4. 测试用例设计综合策略</p>
<p>　　1.概述</p>
<p>　　Grenford J. Myers在《The Art of Software <a onclick="javascript:tagshow(event, 'Testing');" href="javascript:;" target=_self><u><strong><font color=#202859>Testing</font></strong></u></a>》一书中提出：一个好的测试用例是指很可能找到迄今为止尚未发现的错误的测试，由此可见测试用例设计<a onclick="javascript:tagshow(event, '%B9%A4%D7%F7');" href="javascript:;" target=_self><u><strong><font color=#202859>工作</font></strong></u></a>在整个测试过程中的地位，我们不能只凭借一些主观或直观的想法来设计测试用例，应该要以一些比较成熟的测试用例设计方法为指导，再加上设计人员个人的经验积累来设计测试用例，二者相结合应该是非常完美的组合。本文所介绍的测试用例设计方法对于测试设计人员将是一个很好的方法指导，当然看完本文也未必能设计出好的测试用例，有了好的方法作为指导后需要更多的实践经验加以巩固和提炼。只有将测试设计思想与丰富的实践经验相融合才能设计出高质量的测试用例，相信你行！</p>
<p>　　本文描述的范围：测试用例基本概念、测试用例设计方法、测试用例设计综合策略。</p>
<p>　　关键词：测试用例、等价类划分、边界值分析、错误推测、因果图、判定表驱动分析、正交实验、功能图分析、场景设计</p>
<p>&#160;</p>
　　读者对象：测试设计人员、测试人员
<p>&#160;</p>
<p>&#160;</p>
　　参考文献：
<p>&#160;</p>
　　1. 《计算机<a onclick="javascript:tagshow(event, '%C8%ED%BC%FE%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>软件测试</font></strong></u></a>技术》 郑人杰
<p>&#160;</p>
　　2. 《The Art of Software Testing》 Grenford J. Myers
<p>&#160;</p>
<p>　　2.测试用例基本概念</p>
<p>　　2.1.测试用例的定义</p>
<p>　　测试用例是为特定的目的而设计的一组测试输入、执行条件和预期的结果。测试用例是执行的最小实体。简单地说，测试用例就是设计一个场景，使软件程序在这种场景下，必须能够正常运行并且达到程序所设计的执行结果。</p>
　　2.2.测试用例的特征
<p>&#160;</p>
<p>　　1.最有可能抓住错误的；</p>
<p>　　2.不是重复的、多余的；</p>
<p>　　3.一组相似测试用例中最有效的；</p>
<p>　　4.既不是太简单，也不是太复杂。</p>
　　2.3.测试用例组成元素
<p>&#160;</p>
<p>　　1.用例ID；</p>
<p>　　2.用例名称；</p>
<p>　　3.测试目的；</p>
<p>　　4.测试级别；</p>
<p>　　5.参考信息；</p>
<p>　　6.测试环境；</p>
<p>　　7.前提条件；</p>
<p>　　8.测试步骤；</p>
<p>　　9.预期结果；</p>
<p>　　10.设计人员。</p>
　　2.4.测试用例设计原则
<p>&#160;</p>
<p>　　1.测试用例的代表性：能够代表并覆盖各种合理的和不合理的、合法的和非法的、边界的和越界的以及极限的输入数据、操作和环境设置等。</p>
<p>　　2.测试结果的可判定性：即测试执行结果的正确性是可判定的，每一个测试用例都应有相应的期望结果。</p>
<p>　　3.测试结果的可再现性：即对同样的测试用例，系统的执行结果应当是相同的。 </p>
<img src ="http://www.cnitblog.com/stomic/aggbug/51761.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-11-21 11:33 <a href="http://www.cnitblog.com/stomic/archive/2008/11/21/51761.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试用例设计白皮书－－判定表驱动分析方法</title><link>http://www.cnitblog.com/stomic/archive/2008/11/21/51760.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 21 Nov 2008 03:32:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/11/21/51760.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/51760.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/11/21/51760.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/51760.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/51760.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 一.方法简介1.定义：判定表是分析和表达多逻辑条件下执行不同操作的情况的工具。2.判定表的优点&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 能够将复杂的问题按照各种可能的情况全部列举出来，简明并避免遗漏。因此，利用判定表能够设计出完整的测试用例集合。&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在一些数据处理问...&nbsp;&nbsp;<a href='http://www.cnitblog.com/stomic/archive/2008/11/21/51760.html'>阅读全文</a><img src ="http://www.cnitblog.com/stomic/aggbug/51760.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-11-21 11:32 <a href="http://www.cnitblog.com/stomic/archive/2008/11/21/51760.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试用例设计白皮书－－功能图分析方法</title><link>http://www.cnitblog.com/stomic/archive/2008/11/21/51758.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 21 Nov 2008 03:30:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/11/21/51758.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/51758.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/11/21/51758.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/51758.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/51758.html</trackback:ping><description><![CDATA[一、方法简介
<p>　　一个程序的功能说明通常由动态说明和静态说明组成。动态说明描述了输入数据的次序或转移的次序。静态说明描述了输入条件与输出条件之间的对应关系。对于较复杂的程序，由于存在大量的组合情况，因此，仅用静态说明组成的规格说明对于<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>测试</font></strong></u></a>来说往往是不够的。必须用动态说明来补充功能说明。功能图方法是用功能图FD 形式化地表示程序的功能说明，并机械地生成功能图的<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4%D3%C3%C0%FD');" href="javascript:;" target=_self><u><strong><font color=#202859>测试用例</font></strong></u></a>。 功能图模型由状态迁移图和逻辑功能模型构成。状态迁移图用于表示输入数据序列以及相应的输出数据。在状态迁移图中，由输入数据和当前状态决定输出数据和后续状态。逻辑功能模型用于表示在状态中输入条件和输出条件之间的对应关系。逻辑功能模型只适合于描述静态说明，输出数据仅由输入数据决定。测试用例则是由测试中经过的一系列状态和在每个状态中必须依靠输入/输出数据满足的一对条件组成。功能图方法其实是是一种黑盒白盒混合用例设计方法。</p>
<p>　　（功能图方法中，要用到逻辑覆盖和路径测试的概念和方法，其属<a onclick="javascript:tagshow(event, '%B0%D7%BA%D0%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>白盒测试</font></strong></u></a>方法中的内容。逻辑覆盖是以程序内部的逻辑结构为基础的测试用例设计方法。该方法要求测试人员对程序的逻辑结构有清楚的了解。由于覆盖测试的目标不同，逻辑覆盖可分为:语句覆盖，判定覆盖，判定-条件覆盖，条件组合覆盖及路径覆盖。下面我们指的逻辑覆盖和路径是功能或系统水平上的，以区别与白盒测试中的程序内部的。）</p>
<p>　　1、功能图</p>
<p>　　功能图由状态迁移图和布尔函数组成。状态迁移图用状态和迁移来描述。一个状态指出数据输入的位置（或时间），而迁移则指明状态的改变。同时要依靠判定表或因果图表示的逻辑功能。例，一个简化的自动出纳机ATM的功能图。</p>
<p>　　2、测试用例生成方法</p>
<p>　　从功能图生成测试用例，得到的测试用例数是可接受的。 问题的关键的是如何从状态迁移图中选取测试用例。 若用节点代替状态，用弧线代替迁移，则状态迁移图就可转化成一个程序的控制流程图形式。问题就转化为程序的路径测试问题（如白盒测试）问题了。</p>
<p>　　3、测试用例生成规则</p>
<p>　　为了把状态迁移（测试路径）的测试用例与逻辑模型（局部测试用例）的测试用例组合起来，从功能图生成实用的测试用例，须定义下面的规则。在一个结构化的状态迁移（SST）中，定义三种形式的循环:顺序，选择和重复。但分辨一个状态迁移中的所有循环是有困难的。（其表示图形省略）。</p>
<p>　　4、从功能图生成测试用例的过程</p>
<p>　　1)生成局部测试用例:在每个状态中，从因果图生成局部测试用例。局部测试用例由原因值（输入数据）组合与对应的结果值（输出数据或状态）构成。</p>
<p>　　2)测试路径生成:利用上面的规则（三种）生成从初始状态到最后状态的测试路径。</p>
<p>　　3)测试用例合成:合成测试路径与功能图中每个状态中的局部测试用例。结果是初始状态到最后状态的一个状态序列，以及每个状态中输入数据与对应输出数据的组合。</p>
<p>　　5、测试用例的合成算法:采用条件构造树。</p>
<p>　　二、实战演习</p>
<p>　　暂无</p>
<img src ="http://www.cnitblog.com/stomic/aggbug/51758.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-11-21 11:30 <a href="http://www.cnitblog.com/stomic/archive/2008/11/21/51758.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试用例设计白皮书－－正交实验设计方法</title><link>http://www.cnitblog.com/stomic/archive/2008/11/21/51759.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 21 Nov 2008 03:30:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/11/21/51759.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/51759.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/11/21/51759.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/51759.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/51759.html</trackback:ping><description><![CDATA[<span style="FONT-WEIGHT: bold">一、方法简介</span>
<p>　　利用因果图来设计<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4%D3%C3%C0%FD');" href="javascript:;" target=_self><u><strong><a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>测试</font></strong></u></a>用例</strong></u></a>时， 作为输入条件的原因与输出结果之间的因果关系，有时很难从软件需求规格说明中得到。往往因果关系非常庞大，以至于据此因果图而得到的测试用例数目多的惊人，给<a onclick="javascript:tagshow(event, '%C8%ED%BC%FE%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>软件测试</font></strong></u></a>带来沉重的负担，为了有效地，合理地减少测试的工时与费用，可利用正交实验设计方法进行测试用例的设计。</p>
<p>　　正交实验设计方法:依据Galois理论，从大量的（实验）数据（测试例）中挑选适量的，有代表性的点（例），从而合理地安排实验（测试）的一种科学实验设计方法。类似的方法有:聚类分析方法，因子方法方法等。</p>
<p>　　利用正交实验设计测试用例的步骤：</p>
<p>　　1.提取功能说明，构造因子--状态表</p>
<p>　　把影响实验指标的条件称为因子。而影响实验因子的条件叫因子的状态。利用正交实验设计方法来设计测试用例时，首先要根据被测试软件的规格说明书找出影响其功能实现的操作对象和外部因素，把他们当作因子，而把各个因子的取值当作状态。对软件需求规格说明中的功能要求进行划分，把整体的概要性的功能要求进行层层分解与展开，分解成具体的有相对独立性的基本的功能要求。这样就可以把被测试软件中所有的因子都确定下来，并为确定个因子的权值提供参考的依据。确定因子与状态是设计测试用例的关键。因此要求尽可能全面的正确的确定取值，以确保测试用例的设计作到完整与有效。</p>
<p>　　2.加权筛选，生成因素分析表</p>
<p>　　对因子与状态的选择可按其重要程度分别加权。可根据各个因子及状态的作用大小，出现频率的大小以及测试的需要，确定权值的大小。</p>
<p>　　3.利用正交表构造测试数据集</p>
<p>　　正交表的推导依据Galois理论（这里省略，需要时可查数理统计方面的教材）。</p>
<p>　　利用正交实验设计方法设计测试用例，比使用等价类划分，边界值分析，因果图等方法有以下优点:节省测试<a onclick="javascript:tagshow(event, '%B9%A4%D7%F7');" href="javascript:;" target=_self><u><strong><font color=#202859>工作</font></strong></u></a>工时；可控制生成的测试用例数量；测试用例具有一定的覆盖率。</p>
<p>　　<span style="FONT-WEIGHT: bold">二、 实战演习</span></p>
<p>　　暂无</p>
<img src ="http://www.cnitblog.com/stomic/aggbug/51759.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-11-21 11:30 <a href="http://www.cnitblog.com/stomic/archive/2008/11/21/51759.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试用例设计白皮书－－边界值分析方法</title><link>http://www.cnitblog.com/stomic/archive/2008/11/21/51757.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 21 Nov 2008 03:29:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/11/21/51757.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/51757.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/11/21/51757.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/51757.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/51757.html</trackback:ping><description><![CDATA[一.方法简介
<p>　　1. 定义：边界值分析法就是对输入或输出的边界值进行<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>测试</font></strong></u></a>的一种<a onclick="javascript:tagshow(event, '%BA%DA%BA%D0%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>黑盒测试</font></strong></u></a>方法。通常边界值分析法是作为对等价类划分法的补充，这种情况下，其<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4%D3%C3%C0%FD');" href="javascript:;" target=_self><u><strong><font color=#202859>测试用例</font></strong></u></a>来自等价类的边界。</p>
　　2. 与等价划分的区别
<p>&#160;</p>
<p>　　1) 边界值分析不是从某等价类中随便挑一个作为代表，而是使这个等价类的每个边界都要作为测试条件。</p>
<p>　　2) 边界值分析不仅考虑输入条件，还要考虑输出空间产生的测试情况。</p>
<p>　　3. 边界值分析方法的考虑：</p>
<p>　　长期的测试<a onclick="javascript:tagshow(event, '%B9%A4%D7%F7');" href="javascript:;" target=_self><u><strong><font color=#202859>工作</font></strong></u></a>经验告诉我们，大量的错误是发生在输入或输出范围的边界上，而不是发生在输入输出范围的内部。因此针对各种边界情况设计测试用例，可以查出更多的错误。</p>
<p>　　使用边界值分析方法设计测试用例，首先应确定边界情况。通常输入和输出等价类的边界，就是应着重测试的边界情况。应当选取正好等于，刚刚大于或刚刚小于边界的值作为测试数据，而不是选取等价类中的典型值或任意值作为测试数据。 </p>
<p>　　4. 常见的边界值</p>
<p>　　1) 对16-bit 的整数而言 32767 和 -32768 是边界</p>
<p>　　2) 屏幕上光标在最左上、最右下位置</p>
<p>　　3) 报表的第一行和最后一行</p>
<p>　　4) 数组元素的第一个和最后一个</p>
<p>　　5) 循环的第 0 次、第 1 次和倒数第 2 次、最后一次</p>
<p>　　5. 边界值分析</p>
<p>　　1) 边界值分析使用与等价类划分法相同的划分，只是边界值分析假定错误更多地存在于划分的边界上，因此在等价类的边界上以及两侧的情况设计测试用例。</p>
<p>　　例：测试计算平方根的函数</p>
<p>　　--输入：实数</p>
<p>　　--输出：实数</p>
<p>　　--规格说明：当输入一个0或比0大的数的时候，返回其正平方根；当输入一个小于0的数时，显示错误信息"平方根非法-输入值小于0"并返回0；库函数Print-Line可以用来输出错误信息。</p>
<p>　　2) 等价类划分：</p>
<p>　　I.可以考虑作出如下划分：</p>
<p>　　a、输入 (i)&lt;0 和 (ii)&gt;=0</p>
<p>　　b、输出 (a)&gt;=0 和 (b) Error</p>
<p>　　II.测试用例有两个：</p>
<p>　　a、输入4，输出2。对应于 (ii) 和 (a) 。</p>
<p>　　b、输入-10，输出0和错误提示。对应于 (i) 和 (b) 。</p>
<p>　　3) 边界值分析：</p>
<p>　　划分(ii)的边界为0和最大正实数；划分(i)的边界为最小负实数和0。由此得到以下测试用例：</p>
<p>　　a、输入 {最小负实数}</p>
<p>　　b、输入 {绝对值很小的负数}</p>
<p>　　c、输入 0</p>
<p>　　d、输入 {绝对值很小的正数}</p>
<p>　　e、输入 {最大正实数}</p>
<p>&#160;</p>
　　4) 通常情况下，<a onclick="javascript:tagshow(event, '%C8%ED%BC%FE%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>软件测试</font></strong></u></a>所包含的边界检验有几种类型：数字、字符、位置、重量、大小、速度、方位、尺寸、空间等。
<p>&#160;</p>
<p>　　5) 相应地，以上类型的边界值应该在：最大/最小、首位/末位、上/下、最快/最慢、最高/最低、 最短/最长、 空/满等情况下。</p>
<p>　　6) 利用边界值作为测试数据</p>
<p>
<table cellSpacing=0 cellPadding=0 width=521 align=center border=1>
    <tbody>
        <tr>
            <td width=48>
            <p>&#160;</p>
            <p align=center><font size=2><strong>项</strong> </font></p>
            </td>
            <td width=96>
            <p align=center><font size=2><strong>边界值</strong> </font></p>
            </td>
            <td width=377>
            <p align=center><font size=2><strong>测试用例的设计思路</strong> </font></p>
            </td>
        </tr>
        <tr>
            <td width=48>
            <p align=center><font size=2>字符 </font></p>
            </td>
            <td width=96>
            <p align=center><font size=2>起始-1个字符/结束+1个字符 </font></p>
            </td>
            <td width=377>
            <p align=center><font size=2>假设一个文本输入区域允许输入1个到255个 字符，输入1个和255个字符作为有效等价类；输入0个和256个字符作为无效等价类，这几个数值都属于边界条件值。 </font></p>
            </td>
        </tr>
        <tr>
            <td width=48>
            <p align=center><font size=2>数值 </font></p>
            </td>
            <td width=96>
            <p align=center><font size=2>最小值-1/最大值+1</font></p>
            </td>
            <td width=377>
            <p align=center><font size=2>假设某软件的数据输入域要求输入5位的数据值，可以使用10000作为最小值、99999作为最大值；然后使用刚好小于5位和大于5位的 数值来作为边界条件。 </font></p>
            </td>
        </tr>
        <tr>
            <td width=48>
            <p align=center><font size=2>空间 </font></p>
            </td>
            <td width=96>
            <p align=center><font size=2>小于空余空间一点/大于满空间一点 </font></p>
            </td>
            <td width=377>
            <p align=center><font size=2>例如在用U盘存储数据时，使用比剩余磁盘空间大一点（几KB）的文件作为边界条件。 </font></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
7) 内部边界值分析：
<p>　　在多数情况下，边界值条件是基于应用程序的功能设计而需要考虑的因素，可以从软件的规格说明或常识中得到，也是最终用户可以很容易发现问题的。然而，在测试用例设计过程中，某些边界值条件是不需要呈现给用户的，或者说用户是很难注意到的，但同时确实属于检验范畴内的边界条件，称为内部边界值条件或子边界值条件。</p>
<p>　　内部边界值条件主要有下面几种：</p>
<p>　　a) 数值的边界值检验：计算机是基于二进制进行工作的，因此，软件的任何数值运算都有一定的范围限制。 </p>
<table cellSpacing=0 cellPadding=0 width=494 align=center border=1>
    <tbody>
        <tr>
            <td vAlign=top width=116>
            <p><font size=2></font></p>
            <p align=center><font size=2><strong>项</strong> </font></p>
            </td>
            <td vAlign=top width=379>
            <p align=center><font size=2><strong>范围或值</strong> </font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=116>
            <p align=center><font size=2>位（bit） </font></p>
            </td>
            <td vAlign=top width=379>
            <p align=center><font size=2>0 或 1</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=116>
            <p align=center><font size=2>字节（byte） </font></p>
            </td>
            <td vAlign=top width=379>
            <p align=center><font size=2>0 ~ 255</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=116>
            <p align=center><font size=2>字（word） </font></p>
            </td>
            <td vAlign=top width=379>
            <p align=center><font size=2>0~65535（单字）或 0~4294967295（双字） </font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=116>
            <p align=center><font size=2>千（K） </font></p>
            </td>
            <td vAlign=top width=379>
            <p align=center><font size=2>1024</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=116>
            <p align=center><font size=2>兆（M） </font></p>
            </td>
            <td vAlign=top width=379>
            <p align=center><font size=2>1048576</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=116>
            <p align=center><font size=2>吉（G） </font></p>
            </td>
            <td vAlign=top width=379>
            <p align=center><font size=2>1073741824</font></p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　 b) 字符的边界值检验：在计算机软件中，字符也是很重要的表示元素，其中ASCII和Unicode是常见的编码方式。下表中列出了一些常用字符对应的ASCII码值。 </p>
<p>&#160;</p>
<table cellSpacing=0 cellPadding=0 width=468 align=center border=1>
    <tbody>
        <tr>
            <td vAlign=top width=112>
            <p>&#160;</p>
            <p align=center><font size=2><strong>字符</strong> </font></p>
            </td>
            <td vAlign=top width=123>
            <p align=center><font size=2><strong>ASCII</strong><strong>码值</strong> </font></p>
            </td>
            <td vAlign=top width=114>
            <p align=center><font size=2><strong>字符</strong> </font></p>
            </td>
            <td vAlign=top width=121>
            <p align=center><font size=2><strong>ASCII</strong><strong>码值</strong> </font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=112>
            <p align=center><font size=2>空 (null)</font></p>
            </td>
            <td vAlign=top width=123>
            <p align=center><font size=2>0</font></p>
            </td>
            <td vAlign=top width=114>
            <p align=center><font size=2>A</font></p>
            </td>
            <td vAlign=top width=121>
            <p align=center><font size=2>65</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=112>
            <p align=center><font size=2>空格 (space)</font></p>
            </td>
            <td vAlign=top width=123>
            <p align=center><font size=2>32</font></p>
            </td>
            <td vAlign=top width=114>
            <p align=center><font size=2>a</font></p>
            </td>
            <td vAlign=top width=121>
            <p align=center><font size=2>97</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=112>
            <p align=center><font size=2>斜杠 ( / )</font></p>
            </td>
            <td vAlign=top width=123>
            <p align=center><font size=2>47</font></p>
            </td>
            <td vAlign=top width=114>
            <p align=center><font size=2>Z</font></p>
            </td>
            <td vAlign=top width=121>
            <p align=center><font size=2>90</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=112>
            <p align=center><font size=2>0</font></p>
            </td>
            <td vAlign=top width=123>
            <p align=center><font size=2>48</font></p>
            </td>
            <td vAlign=top width=114>
            <p align=center><font size=2>z</font></p>
            </td>
            <td vAlign=top width=121>
            <p align=center><font size=2>122</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=112>
            <p align=center><font size=2>冒号 ( : )</font></p>
            </td>
            <td vAlign=top width=123>
            <p align=center><font size=2>58</font></p>
            </td>
            <td vAlign=top width=114>
            <p align=center><font size=2>单引号 ( &#8216; )</font></p>
            </td>
            <td vAlign=top width=121>
            <p align=center><font size=2>96</font></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=112>
            <p align=center><font size=2>@</font></p>
            </td>
            <td vAlign=top width=123>
            <p align=center><font size=2>64</font></p>
            </td>
            <td vAlign=top width=114>
            <p align=center><font size=2>&nbsp;</font></p>
            </td>
            <td vAlign=top width=121>
            <p align=center>&nbsp;</p>
            </td>
        </tr>
    </tbody>
</table>
　　c) 其它边界值检验
<p>&#160;</p>
<p>&#160;</p>
<p>　　6. 基于边界值分析方法选择测试用例的原则</p>
<p>　　1) 如果输入条件规定了值的范围,则应取刚达到这个范围的边界的值,以及刚刚超越这个范围边界的值作为测试输入数据。</p>
<p>　　例如，如果程序的规格说明中规定："重量在10公斤至50公斤范围内的邮件，其邮费计算公式为&#8230;&#8230;"。作为测试用例，我们应取10及50，还应取10.01,49.99,9.99及50.01等。</p>
<p>　　2) 如果输入条件规定了值的个数,则用最大个数,最小个数,比最小个数少一,比最大个数多一的数作为测试数据。</p>
比如，一个输入文件应包括1~255个记录，则测试用例可取1和255，还应取0及256等。
<p>&#160;</p>
<p>　　3) 将规则1）和2）应用于输出条件，即设计测试用例使输出值达到边界值及其左右的值。</p>
<p>　　例如，某程序的规格说明要求计算出"每月保险金扣除额为0至1165.25元"，其测试用例可取0.00及1165.24、还可取一0.01及1165．26等。</p>
<p>　　再如一程序属于情报检索系统，要求每次"最少显示1条、最多显示4条情报摘要"，这时我们应考虑的测试用例包括1和4，还应包括0和5等。 </p>
<p>　　4) 如果程序的规格说明给出的输入域或输出域是有序集合,则应选取集合的第一个元素和最后一个元素作为测试用例。</p>
<p>　　5) 如果程序中使用了一个内部数据结构,则应当选择这个内部数据结构的边界上的值作为测试用例。</p>
<p>　　6) 分析规格说明,找出其它可能的边界条件。 </p>
<p>　　<strong>二</strong><strong>.</strong><strong>实战演习</strong></p>
<p>　　1.现有一个学生标准化考试批阅试卷,产生成绩报告的程序。其规格说明如下:程序的输入文件由一些有80个字符的记录组成,如右图所示，所有记录分为3组： </p>
<p align=center><img title=点击图片可在新窗口打开 style="CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081113/03.jpg"></p>
① 标题：这一组只有一个记录，其内容为输出成绩报告的名字。
<p>　　② 试卷各题标准答案记录：每个记录均在第80个字符处标以数字"2"。该组的第一个记录的第1至第3个字符为题目编号（取值为1一999）。第10至第59个字符给出第1至第50题的答案（每个合法字符表示一个答案）。该组的第2，第3&#8230;&#8230;个记录相应为第51至第100，第101至第150，&#8230;题的答案。</p>
<p>　　③ 每个学生的答卷描述：该组中每个记录的第80个字符均为数字"3"。每个学生的答卷在若干个记录中给出。如甲的首记录第1至第9字符给出学生姓名及学号，第10至第59字符列出的是甲所做的第1至第50题的答案。若试题数超过50，则第2，第3&#8230;&#8230;纪录分别给出他的第51至第100，第101至第150&#8230;&#8230;题的解答。然后是学生乙的答卷记录。</p>
<p>　　④ 学生人数不超过200，试题数不超过999。 </p>
<p>　　⑤ 程序的输出有4个报告：</p>
<p>　　a) 按学号排列的成绩单，列出每个学生的成绩、名次。</p>
<p>　　b) 按学生成绩排序的成绩单。</p>
<p>　　c) 平均分数及标准偏差的报告。</p>
<p>　　d) 试题分析报告。按试题号排序，列出各题学生答对的百分比。 </p>
<p>　　解答：分别考虑输入条件和输出条件，以及边界条件。给出下表所示的输入条件及相应的测试用例。 </p>
<p align=center><img title=点击图片可在新窗口打开 style="CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081113/01.jpg"></p>
<p>　　输出条件及相应的测试用例表。 </p>
<p align=center><img title=点击图片可在新窗口打开 style="CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081113/02.jpg"></p>
<p>　　2.三角形问题的边界值分析测试用例</p>
<p>　　在三角形问题描述中，除了要求边长是整数外，没有给出其它的限制条件。在此，我们将三角形每边边长的取范围值设值为[1, 100] 。</p>
<p>&#160;</p>
<p>　　
<table style="FONT-SIZE: 12px" cellSpacing=0 cellPadding=0 width="75%" align=center border=1>
    <tbody>
        <tr>
            <td vAlign=top width=82>
            <p align=center><strong>测试用例</strong> </p>
            </td>
            <td vAlign=top width=93>
            <p align=center><strong>a</strong> </p>
            </td>
            <td vAlign=top width=100>
            <p align=center><strong>b</strong> </p>
            </td>
            <td vAlign=top width=100>
            <p align=center><strong>c</strong> </p>
            </td>
            <td vAlign=top width=113>
            <p align=center><strong>预期输出</strong> </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=82>
            <p align=center>Test1</p>
            <p align=center>Test2 </p>
            <p align=center>Test3 </p>
            <p align=center>Test4 </p>
            <p align=center>Test5 </p>
            </td>
            <td vAlign=top width=93>
            <p align=center>60</p>
            <p align=center>60 </p>
            <p align=center>60 </p>
            <p align=center>50 </p>
            <p align=center>50 </p>
            </td>
            <td vAlign=top width=100>
            <p align=center>60</p>
            <p align=center>60 </p>
            <p align=center>60 </p>
            <p align=center>50 </p>
            <p align=center>50 </p>
            </td>
            <td vAlign=top width=100>
            <p align=center>1</p>
            <p align=center>2 </p>
            <p align=center>60 </p>
            <p align=center>99 </p>
            <p align=center>100 </p>
            </td>
            <td vAlign=top width=113>
            <p align=center>等腰三角形 </p>
            <p align=center>等腰三角形 </p>
            <p align=center>等边三角形 </p>
            <p align=center>等腰三角形 </p>
            <p align=center>非三角形 </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=82>
            <p align=center>Test6</p>
            <p align=center>Test7 </p>
            <p align=center>Test8 </p>
            <p align=center>Test9 </p>
            </td>
            <td vAlign=top width=93>
            <p align=center>60</p>
            <p align=center>60 </p>
            <p align=center>50 </p>
            <p align=center>50 </p>
            </td>
            <td vAlign=top width=100>
            <p align=center>1</p>
            <p align=center>2 </p>
            <p align=center>99 </p>
            <p align=center>100 </p>
            </td>
            <td vAlign=top width=100>
            <p align=center>60</p>
            <p align=center>60 </p>
            <p align=center>50 </p>
            <p align=center>50 </p>
            </td>
            <td vAlign=top width=113>
            <p align=center>等腰三角形 </p>
            <p align=center>等腰三角形 </p>
            <p align=center>等腰三角形 </p>
            <p align=center>非三角形 </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=82>
            <p align=center>Test10</p>
            <p align=center>Test11 </p>
            <p align=center>Test12 </p>
            <p align=center>Test13 </p>
            </td>
            <td vAlign=top width=93>
            <p align=center>1</p>
            <p align=center>2 </p>
            <p align=center>99 </p>
            <p align=center>100 </p>
            </td>
            <td vAlign=top width=100>
            <p align=center>60</p>
            <p align=center>60 </p>
            <p align=center>50 </p>
            <p align=center>50 </p>
            </td>
            <td vAlign=top width=100>
            <p align=center>60</p>
            <p align=center>60 </p>
            <p align=center>50 </p>
            <p align=center>50 </p>
            </td>
            <td vAlign=top width=113>
            <p align=center>等腰三角形 </p>
            <p align=center>等腰三角形 </p>
            <p align=center>等腰三角形 </p>
            <p align=center>非三角形</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&#160;</p>
<p>　　3.NextDate函数的边界值分析测试用例</p>
<p>　　在NextDate函数中，隐含规定了变量mouth和变量day的取值范围为1&#8804;mouth&#8804;12和1&#8804;day&#8804;31，并设定变量year的取值范围为1912&#8804;year&#8804;2050 。 </p>
<p align=center>
<table style="FONT-SIZE: 12px" cellSpacing=0 cellPadding=0 width="75%" border=1>
    <tbody>
        <tr>
            <td vAlign=top width=97>
            <p align=center><strong>测试用例</strong> </p>
            </td>
            <td vAlign=top width=75>
            <p align=center><strong>mouth</strong> </p>
            </td>
            <td vAlign=top width=88>
            <p align=center><strong>day</strong> </p>
            </td>
            <td vAlign=top width=92>
            <p align=center><strong>year</strong> </p>
            </td>
            <td vAlign=top width=154>
            <p align=center><strong>预期输出</strong> </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=97>
            <p align=center>Test1</p>
            <p align=center>Test2 </p>
            <p align=center>Test3 </p>
            <p align=center>Test4 </p>
            <p align=center>Test5 </p>
            <p align=center>Test6 </p>
            <p align=center>Test7 </p>
            </td>
            <td vAlign=top width=75>
            <p align=center>6</p>
            <p align=center>6 </p>
            <p align=center>6 </p>
            <p align=center>6 </p>
            <p align=center>6 </p>
            <p align=center>6 </p>
            <p align=center>6 </p>
            </td>
            <td vAlign=top width=88>
            <p align=center>15</p>
            <p align=center>15 </p>
            <p align=center>15 </p>
            <p align=center>15 </p>
            <p align=center>15 </p>
            <p align=center>15 </p>
            <p align=center>15 </p>
            </td>
            <td vAlign=top width=92>
            <p align=center>1911</p>
            <p align=center>1912 </p>
            <p align=center>1913 </p>
            <p align=center>1975 </p>
            <p align=center>2049 </p>
            <p align=center>2050 </p>
            <p align=center>2051 </p>
            </td>
            <td vAlign=top width=154>
            <p align=center>1911.6.16</p>
            <p align=center>1912.6.16 </p>
            <p align=center>1913.6.16 </p>
            <p align=center>1975.6.16 </p>
            <p align=center>2049.6.16 </p>
            <p align=center>2050.6.16 </p>
            <p align=center>2051.6.16 </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=97>
            <p align=center>Test8</p>
            <p align=center>Test9 </p>
            <p align=center>Test10 </p>
            <p align=center>Test11 </p>
            <p align=center>Test12 </p>
            <p align=center>Test13 </p>
            </td>
            <td vAlign=top width=75>
            <p align=center>6</p>
            <p align=center>6 </p>
            <p align=center>6 </p>
            <p align=center>6 </p>
            <p align=center>6 </p>
            <p align=center>6 </p>
            </td>
            <td vAlign=top width=88>
            <p align=center>-1</p>
            <p align=center>1 </p>
            <p align=center>2 </p>
            <p align=center>30 </p>
            <p align=center>31 </p>
            <p align=center>32 </p>
            </td>
            <td vAlign=top width=92>
            <p align=center>2001</p>
            <p align=center>2001 </p>
            <p align=center>2001 </p>
            <p align=center>2001 </p>
            <p align=center>2001 </p>
            <p align=center>2001 </p>
            </td>
            <td vAlign=top width=154>
            <p align=center>day超出[1&#8230;31]</p>
            <p align=center>2001.6.2 </p>
            <p align=center>2001.6.3 </p>
            <p align=center>2001.7.1 </p>
            <p align=center>输入日期超界 </p>
            <p align=center>day超出[1&#8230;31] </p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=97>
            <p align=center>Test14</p>
            <p align=center>Test15 </p>
            <p align=center>Test16 </p>
            <p align=center>Test17 </p>
            <p align=center>Test18 </p>
            <p align=center>Test19 </p>
            </td>
            <td vAlign=top width=75>
            <p align=center>-1</p>
            <p align=center>1 </p>
            <p align=center>2 </p>
            <p align=center>11 </p>
            <p align=center>12 </p>
            <p align=center>13 </p>
            </td>
            <td vAlign=top width=88>
            <p align=center>15</p>
            <p align=center>15 </p>
            <p align=center>15 </p>
            <p align=center>15 </p>
            <p align=center>15 </p>
            <p align=center>15 </p>
            </td>
            <td vAlign=top width=92>
            <p align=center>2001</p>
            <p align=center>2001 </p>
            <p align=center>2001 </p>
            <p align=center>2001 </p>
            <p align=center>2001 </p>
            <p align=center>2001 </p>
            </td>
            <td vAlign=top width=154>
            <p align=center>Mouth超出[1&#8230;12]</p>
            <p align=center>2001.1.16 </p>
            <p align=center>2001.2.16 </p>
            <p align=center>2001.11.16 </p>
            <p align=center>2001.12.16 </p>
            <p align=center>Mouth超出[1&#8230;12]</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>&nbsp;</p>
<img src ="http://www.cnitblog.com/stomic/aggbug/51757.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-11-21 11:29 <a href="http://www.cnitblog.com/stomic/archive/2008/11/21/51757.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试用例设计白皮书－－等价类划分方法</title><link>http://www.cnitblog.com/stomic/archive/2008/11/21/51756.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 21 Nov 2008 03:28:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/11/21/51756.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/51756.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/11/21/51756.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/51756.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/51756.html</trackback:ping><description><![CDATA[<strong>一.方法简介</strong>
<p>　　1.定义</p>
<p>　　是把所有可能的输入数据,即程序的输入域划分成若干部分（子集）,然后从每一个子集中选取少数具有代表性的数据作为<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4%D3%C3%C0%FD');" href="javascript:;" target=_self><u><strong><a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>测试</font></strong></u></a>用例</strong></u></a>。该方法是一种重要的,常用的<a onclick="javascript:tagshow(event, '%BA%DA%BA%D0%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>黑盒测试</font></strong></u></a>用例设计方法。</p>
<p>　　2.划分等价类：</p>
<p>　　等价类是指某个输入域的子集合。在该子集合中,各个输入数据对于揭露程序中的错误都是等效的，并合理地假定：测试某等价类的代表值就等于对这一类<a onclick="javascript:tagshow(event, '%C6%E4%CB%FC');" href="javascript:;" target=_self><u><strong><font color=#202859>其它</font></strong></u></a>值的测试，因此,可以把全部输入数据合理划分为若干等价类,在每一个等价类中取一个数据作为测试的输入条件就可以用少量代表性的测试数据取得较好的测试结果。等价类划分可有两种不同的情况：有效等价类和无效等价类。</p>
<p>　　1)有效等价类</p>
<p>　　是指对于程序的规格说明来说是合理的、有意义的输入数据构成的集合。利用有效等价类可检验程序是否实现了规格说明中所规定的功能和性能。</p>
<p>　　2)无效等价类</p>
<p>　　与有效等价类的定义恰巧相反。无效等价类指对程序的规格说明是不合理的或无意义的输入数据所构成的集合。对于具体的问题，无效等价类至少应有一个，也可能有多个。</p>
<p>　　设计测试用例时,要同时考虑这两种等价类。因为软件不仅要能接收合理的数据,也要能经受意外的考验，这样的测试才能确保软件具有更高的可靠性。</p>
<p>　　3.划分等价类的标准：</p>
<p>　　1)完备测试、避免冗余;</p>
<p>　　2)划分等价类重要的是：集合的划分，划分为互不相交的一组子集，而子集的并是整个集合;</p>
<p>　　3)并是整个集合：完备性;</p>
<p>　　4)子集互不相交：保证一种形式的无冗余性;</p>
<p>　　5)同一类中标识（选择）一个测试用例，同一等价类中，往往处理相同，相同处理映射到"相同的执行路径"。 </p>
<p>　　4.划分等价类的方法</p>
<p>　　1)在输入条件规定了取值范围或值的个数的情况下,则可以确立一个有效等价类和两个无效等价类。如：输入值是学生成绩，范围是0～100；</p>
<p align=center><img title=点击图片可在新窗口打开 style="WIDTH: 500px; CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081117/01.jpg"></p>
<p>　　2)在输入条件规定了输入值的集合或者规定了"必须如何"的条件的情况下,可确立一个有效等价类和一个无效等价类；</p>
<p>　　3)在输入条件是一个布尔量的情况下,可确定一个有效等价类和一个无效等价类。</p>
<p>　　4)在规定了输入数据的一组值（假定n个）,并且程序要对每一个输入值分别处理的情况下,可确立n个有效等价类和一个无效等价类。</p>
<p>　　例：输入条件说明学历可为:专科、本科、硕士、博士四种之一，则分别取这四种这四个值作为四个有效等价类，另外把四种学历之外的任何学历作为无效等价类。</p>
<p>　　5)在规定了输入数据必须遵守的规则的情况下,可确立一个有效等价类（符合规则）和若干个无效等价类（从不同角度违反规则）；</p>
<p>　　6)在确知已划分的等价类中各元素在程序处理中的方式不同的情况下,则应再将该等价类进一步的划分为更小的等价类。</p>
<p>　　5.设计测试用例</p>
<p>　　在确立了等价类后,可建立等价类表,列出所有划分出的等价类输入条件：有效等价类、无效等价类，然后从划分出的等价类中按以下三个原则设计测试用例：</p>
<p>　　1)为每一个等价类规定一个唯一的编号；</p>
<p>　　2)设计一个新的测试用例,使其尽可能多地覆盖尚未被覆盖地有效等价类,重复这一步，直到所有的有效等价类都被覆盖为止；</p>
<p>　　3)设计一个新的测试用例,使其仅覆盖一个尚未被覆盖的无效等价类,重复这一步，直到所有的无效等价类都被覆盖为止。</p>
<strong>二.实战演习</strong>
<p>　　1.某程序规定："输入三个整数 a 、 b 、 c 分别作为三边的边长构成三角形。通过程序判定所构成的三角形的类型，当此三角形为一般三角形、等腰三角形及等边三角形时，分别作计算 &#8230; "。用等价类划分方法为该程序进行测试用例设计。（三角形问题的复杂之处在于输入与输出之间的关系比较复杂。）</p>
<p>　　分析题目中给出和隐含的对输入条件的要求： </p>
<p>　　（1）整数 （2）三个数 （3）非零数 （4）正数 </p>
<p>　　（5）两边之和大于第三边 （6）等腰 （7）等边 </p>
<p>　　如果 a 、 b 、 c 满足条件（ 1 ） ~ （ 4 ），则输出下列四种情况之一：</p>
<p>　　1)如果不满足条件（5），则程序输出为 " 非三角形 " 。</p>
<p>　　2)如果三条边相等即满足条件（7），则程序输出为 " 等边三角形 " 。</p>
<p>　　3)如果只有两条边相等、即满足条件（6），则程序输出为 " 等腰三角形 " 。</p>
<p>　　4)如果三条边都不相等，则程序输出为 " 一般三角形 " 。 </p>
<p>　　列出等价类表并编号 </p>
<p align=center><img title=点击图片可在新窗口打开 style="CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081117/02.jpg"></p>
<p>　　 覆盖有效等价类的测试用例：</p>
<p>　　a b c 覆盖等价类号码</p>
<p>　　3 4 5 （1）--（7）</p>
<p>　　4 4 5 （1）--（7），（8）</p>
<p>　　4 5 5 （1）--（7），（9） </p>
<p>　　5 4 5 （1）--（7），（10）</p>
<p>　　4 4 4 （1）--（7），（11）</p>
<p>　　覆盖无效等价类的测试用例： </p>
<p align=center><img title=点击图片可在新窗口打开 style="WIDTH: 500px; CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081117/03.jpg"></p>
2.设有一个档案管理系统，要求用户输入以年月表示的日期。假设日期限定在1990年1月~2049年12月，并规定日期由6位数字字符组成，前4位表示年，后2位表示月。现用等价类划分法设计测试用例，来测试程序的"日期检查功能"。
<p>　　1)划分等价类并编号,下表等价类划分的结果 </p>
<p>
<table align=center border=1>
    <tbody>
        <tr>
            <td>
            <p align=center><font size=2><strong>输入等价类</strong> </font></p>
            </td>
            <td>
            <p align=center><font size=2><strong>有效等价类</strong> </font></p>
            </td>
            <td>
            <p align=center><font size=2><strong>无效等价类</strong> </font></p>
            </td>
        </tr>
        <tr>
            <td>
            <p align=center><font size=2>　　日期的类型及长度 </font></p>
            </td>
            <td>
            <p align=center><font size=2>　　①6位数字字符 </font></p>
            </td>
            <td>
            <p align=center><font size=2>②有非数字字符</font></p>
            <p align=center><font size=2>③少于6位数字字符 </font></p>
            <p align=center><font size=2>④多于6位数字字符 </font></p>
            </td>
        </tr>
        <tr>
            <td>
            <p align=center><font size=2>　年份范围 </font></p>
            </td>
            <td>
            <p align=center><font size=2>　　⑤在1990~2049之间 </font></p>
            </td>
            <td>
            <p align=center><font size=2>⑥小于1990<br>⑦大于2049</font></p>
            </td>
        </tr>
        <tr>
            <td>
            <p align=center><font size=2>　月份范围 </font></p>
            </td>
            <td>
            <p align=center><font size=2>　　⑧在01~12之间 </font></p>
            </td>
            <td>
            <p align=center><font size=2>⑨等于00</font>
            <p align=center><font size=2>⑩大于12</font></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>　　2)设计测试用例，以便覆盖所有的有效等价类在表中列出了3个有效等价类，编号分别为①、⑤、⑧，设计的测试用例如下：</p>
<p>　　测试数据 期望结果 覆盖的有效等价类</p>
<p>
<table style="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid; BACKGROUND-COLOR: #dddddd" align=center>
    <tbody>
        <tr>
            <td>200211 输入有效 ①、⑤、⑧</td>
        </tr>
    </tbody>
</table>
</p>
<p>　　3)为每一个无效等价类设计一个测试用例，设计结果如下：</p>
<p>　　测试数据 期望结果 覆盖的无效等价类</p>
<p>
<table style="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid; BACKGROUND-COLOR: #dddddd" align=center>
    <tbody>
        <tr>
            <td>95June 无效输入 ②
            <p>20036 无效输入③</p>
            <p>2001006无效输入 ④</p>
            <p>198912 无效输入 ⑥</p>
            <p>200401 无效输入 ⑦</p>
            <p>200100 无效输入 ⑨</p>
            <p>200113 无效输入 ⑩</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>　　3.NextDate 函数包含三个变量：month 、 day 和 year ，函数的输出为输入日期后一天的日期。 例如，输入为 2006年3月 7日，则函数的输出为 2006年3月8日 。要求输入变量 month 、 day 和 year 均为整数值，并且满足下列条件：</p>
<p>
<table style="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid; BACKGROUND-COLOR: #dddddd" align=center>
    <tbody>
        <tr>
            <td>①1&#8804;month&#8804;12
            <p>②1&#8804;day&#8804;31</p>
            <p>③1920&#8804;year&#8804;2050 </p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>　　1)有效等价类为：</p>
<p>　　
<table style="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid; BACKGROUND-COLOR: #dddddd" align=center>
    <tbody>
        <tr>
            <td>M1＝{月份：1&#8804;月份&#8804;12}
            <p>D1＝{日期：1&#8804;日期&#8804;31}</p>
            <p>Y1＝{年：1812&#8804;年&#8804;2012}</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>　　2)若条件 ① ~ ③中任何一个条件失效，则 NextDate 函数都会产生一个输出，指明相应的变量超出取值范围，比如 "month 的值不在 1-12 范围当中 " 。显然还存在着大量的 year 、 month 、 day 的无效组合， NextDate 函数将这些组合作统一的输出： " 无效输入日期 " 。其无效等价类为：</p>
<p>
<table style="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid; BACKGROUND-COLOR: #dddddd" align=center>
    <tbody>
        <tr>
            <td>M2＝{月份：月份&lt;1}
            <p>M3＝{月份：月份&gt;12}</p>
            <p>D2＝{日期：日期&lt;1}</p>
            <p>D3＝{日期：日期&gt;31}</p>
            <p>Y2＝{年：年&lt;1812}</p>
            <p>Y3＝{年：年&gt;2012}</p>
            <p>弱一般等价类测试用例</p>
            <p>月份 日期 年 预期输出</p>
            <p>6 15 1912 1912年6月16日</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>　　强一般等价类测试用例同弱一般等价类测试用例</p>
<p>　　注：弱--有单缺陷假设；健壮--考虑了无效值 </p>
<p>　　(一)弱健壮等价类测</p>
<p>　　
<table style="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid; BACKGROUND-COLOR: #dddddd" align=center>
    <tbody>
        <tr>
            <td>用例ID 月份 日期 年 预期输出
            <p>WR1 6 15 1912 1912年6月16日</p>
            <p>WR2 -1 15 1912 月份不在1～12中</p>
            <p>WR3 13 15 1912 月份不在1～12中</p>
            <p>WR4 6 -1 1912 日期不在1～31中</p>
            <p>WR5 6 32 1912 日期不在1～31中</p>
            <p>WR6 6 15 1811 年份不在1812～2012中</p>
            <p>WR7 6 15 2013 年份不在1812～2012中 </p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>　　(二)强健壮等价类测试</p>
<p>　　
<table style="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid; BACKGROUND-COLOR: #dddddd" align=center>
    <tbody>
        <tr>
            <td>用例ID 月份 日期 年 预期输出
            <p>SR1 -1 15 1912 月份不在1～12中</p>
            <p>SR2 6 -1 1912 日期不在1～31中</p>
            <p>SR3 6 15 1811 年份不在1812～2012中</p>
            <p>SR4 -1 -11912 两个无效一个有效</p>
            <p>SR5 6 -1 1811 两个无效一个有效</p>
            <p>SR6 -1 15 1811 两个无效一个有效</p>
            <p>SR7 -1 -11811 三个无效<br></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>　　4.佣金问题等价类测试用例，它是根据佣金函数的输出值域定义等价类，来改进测试用例集合。</p>
<p>
<table style="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid; BACKGROUND-COLOR: #dddddd" align=center>
    <tbody>
        <tr>
            <td>输出销售额&#8804;1000元 佣金10％
            <p>　　1000&lt;销售额&#8804;1800 佣金=100+(销售额-1000)*15%</p>
            <p>　　销售额&gt;1800 佣金=220+(销售额-1800)*20%</p>
            <p>　　测试用例 枪机(45) 枪托(30) 枪管(25) 销售额 佣金</p>
            </td>
        </tr>
    </tbody>
</table>
　　
<table style="BORDER-RIGHT: #999 1px solid; BORDER-TOP: #999 1px solid; FONT-SIZE: 12px; BORDER-LEFT: #999 1px solid; WIDTH: 80%; BORDER-BOTTOM: #999 1px solid; BACKGROUND-COLOR: #dddddd" align=center>
    <tbody>
        <tr>
            <td>1 5 5 5 500 50
            <p>2 15 15 15 1500 175</p>
            <p>3 25 25 25 2500 360</p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>　　根据输出域选择输入值，使落在输出域等价类内，可以结合弱健壮测试用例结合。</p>
<img src ="http://www.cnitblog.com/stomic/aggbug/51756.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-11-21 11:28 <a href="http://www.cnitblog.com/stomic/archive/2008/11/21/51756.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试用例设计白皮书－－错误推测方法</title><link>http://www.cnitblog.com/stomic/archive/2008/11/21/51755.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 21 Nov 2008 03:26:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/11/21/51755.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/51755.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/11/21/51755.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/51755.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/51755.html</trackback:ping><description><![CDATA[<strong>一. 方法简介</strong>
<p>　　1. 定义：基于经验和直觉推测程序中所有可能存在的各种错误, 从而有针对性的设计<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4%D3%C3%C0%FD');" href="javascript:;" target=_self><u><strong><a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>测试</font></strong></u></a>用例</strong></u></a>的方法。</p>
<p>　　2. 错误推测方法的基本思想：</p>
<p>　　列举出程序中所有可能有的错误和容易发生错误的特殊情况,根据他们选择测试用例。</p>
<p>　　1) 例如, 输入数据和输出数据为0的情况；输入表格为空格或输入表格只有一行。 这些都是容易发生错误的情况。可选择这些情况下的例子作为测试用例。</p>
<p>　　2) 例如，前面例子中成绩报告的程序，采用错误推测法还可补充设计一些测试用例：</p>
<p>　　I.程序是否把空格作为回答</p>
<p>　　II. 在回答记录中混有标准答案记录</p>
<p>　　III.除了标题记录外，还有一些的记录最后一个字符即不是2也不是3</p>
<p>　　IV.有两个学生的学号相同</p>
<p>　　V. 试题数是负数。 </p>
<p>　　3) 再如，测试一个对线性表（比如数组）进行排序的程序，可推测列出以下几项需要特别测试的情况：</p>
<p>　　I.输入的线性表为空表；</p>
<p>　　II. 表中只含有一个元素；</p>
<p>　　III.输入表中所有元素已排好序；</p>
<p>　　IV.输入表已按逆序排好；</p>
<p>　　V. 输入表中部分或全部元素相同。</p>
<p>　　<strong>二. 实战演习</strong></p>
<p>　　 暂无<br></p>
<img src ="http://www.cnitblog.com/stomic/aggbug/51755.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-11-21 11:26 <a href="http://www.cnitblog.com/stomic/archive/2008/11/21/51755.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试用例设计白皮书－－因果图方法</title><link>http://www.cnitblog.com/stomic/archive/2008/11/21/51754.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 21 Nov 2008 03:25:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/11/21/51754.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/51754.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/11/21/51754.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/51754.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/51754.html</trackback:ping><description><![CDATA[<strong>一. 方法简介</strong>
<p>　　1.定义：是一种利用图解法分析输入的各种组合情况，从而设计<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>测试</font></strong></u></a>用例的方法，它适合于检查程序输入条件的各种组合情况。 </p>
<p>　　2.因果图法产生的背景： </p>
<p>　　等价类划分法和边界值分析方法都是着重考虑输入条件，但没有考虑输入条件的各种组合、输入条件之间的相互制约关系。这样虽然各种输入条件可能出错的情况已经测试到了，但多个输入条件组合起来可能出错的情况却被忽视了。</p>
<p>　　如果在测试时必须考虑输入条件的各种组合，则可能的组合数目将是天文数字，因此必须考虑采用一种适合于描述多种条件的组合、相应产生多个动作的形式来进行测试用例的设计，这就需要利用因果图（逻辑模型）。 </p>
<p>　　3.因果图介绍 </p>
<p>　　1) 4种符号分别表示了规格说明中向4种因果关系。</p>
<p align=center><img title=点击图片可在新窗口打开 style="CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081118/01.jpg"></p>
<p>　　2) 因果图中使用了简单的逻辑符号，以直线联接左右结点。左结点表示输入状态（或称原因），右结点表示输出状态（或称结果）。 </p>
<p>　　3) Ci表示原因，通常置于图的左部；ei表示结果，通常在图的右部。Ci和ei均可取值0或1，0表示某状态不出现，1表示某状态出现。 </p>
<p>　　4. 因果图概念 </p>
<p>　　1) 关系 </p>
<p>　　① 恒等：若ci是1，则ei也是1；否则ei为0。 </p>
<p>　　② 非：若ci是1，则ei是0；否则ei是1。 </p>
<p>　　③ 或：若c1或c2或c3是1，则ei是1；否则ei为0。&#8220;或&#8221;可有任意个输入。</p>
<p>　　④ 与：若c1和c2都是1，则ei为1；否则ei为0。&#8220;与&#8221;也可有任意个输入。</p>
<p>　　 2) 约束 </p>
<p>　　输入状态相互之间还可能存在某些依赖关系，称为约束。例如, 某些输入条件本身不可能同时出现。输出状态之间也往往存在约束。在因果图中,用特定的符号标明这些约束。 </p>
<p align=center><img title=点击图片可在新窗口打开 style="CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081118/02.jpg"></p>
<p>　　A.输入条件的约束有以下4类： </p>
<p>　　① E约束（异）：a和b中至多有一个可能为1，即a和b不能同时为1。 </p>
<p>　　② I约束（或）：a、b和c中至少有一个必须是1，即 a、b 和c不能同时为0。 </p>
<p>　　③ O约束（唯一）；a和b必须有一个，且仅有1个为1。 </p>
<p>　　④ R约束（要求）：a是1时，b必须是1，即不可能a是1时b是0。 </p>
<p>　　B.输出条件约束类型 </p>
<p>　　输出条件的约束只有M约束（强制）：若结果a是1，则结果b强制为0。 </p>
<p>　　5. 采用因果图法设计测试用例的步骤： </p>
<p>　　1) 分析软件规格说明描述中, 那些是原因(即输入条件或输入条件的等价类),那些是结果(即输出条件), 并给每个原因和结果赋予一个标识符。</p>
<p>　　2) 分析软件规格说明描述中的语义，找出原因与结果之间, 原因与原因之间对应的关系，根据这些关系,画出因果图。</p>
<p>　　3) 由于语法或环境限制, 有些原因与原因之间,原因与结果之间的组合情况不可能出现，为表明这些特殊情况, 在因果图上用一些记号表明约束或限制条件。 </p>
<p>　　4) 把因果图转换为判定表。</p>
<p>　　5) 把判定表的每一列拿出来作为依据,设计测试用例。 </p>
<strong>二. 实战演习</strong>
<p>　　1. 某软件规格说明书包含这样的要求：第一列字符必须是A或B，第二列字符必须是一个数字，在此情况下进行文件的修改，但如果第一列字符不正确，则给出信息L；如果第二列字符不是数字，则给出信息。 </p>
<p>　　解答：</p>
<p>　　1) 根据题意，原因和结果如下： </p>
<p>　　原因： </p>
<p>　　1——第一列字符是A； </p>
<p>　　2——第一列字符是B； </p>
<p>　　3——第二列字符是一数字。</p>
<p>　　结果： </p>
<p>　　21——修改文件；</p>
<p>　　22 ——给出信息L；</p>
<p>　　23——给出信息M。</p>
<p>　　2) 其对应的因果图如下：</p>
<p>　　11为中间节点；考虑到原因1和原因2不可能同时为1，因此在因果图上施加E约束。</p>
<p align=center><img title=点击图片可在新窗口打开 style="CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081118/03.jpg"></p>
<p>　　3) 根据因果图建立判定表。 </p>
<p align=center><img title=点击图片可在新窗口打开 style="CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081118/04.jpg"></p>
<p>　　表中8种情况的左面两列情况中，原因①和原因②同时为1，这是不可能出现的，故应排除这两种情况。表的最下一栏给出了6种情况的测试用例，这是我们所需要的数据。</p>
<p>　　2. 有一个处理单价为5角钱的饮料的自动售货机软件测试用例的设计。其规格说明如下：若投入5角钱或1元钱的硬币，押下〖橙汁〗或〖啤酒〗的按钮，则相应的饮料就送出来。若售货机没有零钱找，则一个显示〖零钱找完〗的红灯亮，这时在投入1元硬币并押下按钮后，饮料不送出来而且1元硬币也退出来；若有零钱找，则显示〖零钱找完〗的红灯灭，在送出饮料的同时退还5角硬币。 </p>
<p>　　1) 分析这一段说明，列出原因和结果 </p>
<p>　　原因：</p>
<p>　　1.售货机有零钱找 </p>
<p>　　2.投入1元硬币 </p>
<p>　　3.投入5角硬币 </p>
<p>　　4.押下橙汁按钮 </p>
<p>　　5.押下啤酒按钮</p>
<p>　　结果： </p>
<p>　　21.售货机〖零钱找完〗灯亮 </p>
<p>　　22.退还1元硬币 </p>
<p>　　23.退还5角硬币 </p>
<p>　　24.送出橙汁饮料 </p>
<p>　　25.送出啤酒饮料 </p>
<p>　　2) 画出因果图，如图所示。所有原因结点列在左边，所有结果结点列在右边。建立中间结点，表示处理的中间状态。中间结点： </p>
<p>　　11. 投入1元硬币且押下饮料按钮 </p>
<p>　　12. 押下〖橙汁〗或〖啤酒〗的按钮 </p>
<p>　　13. 应当找5角零钱并且售货机有零钱找 </p>
<p>　　14. 钱已付清 </p>
<p align=center><img title=点击图片可在新窗口打开 style="CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081118/05.jpg"></p>
<p>　　3) 转换成判定表： </p>
<p align=center><img title=点击图片可在新窗口打开 style="CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081118/06.jpg"></p>
<p>　　4) 在判定表中，阴影部分表示因违反约束条件的不可能出现的情况，删去。第16列与第32列因什么动作也没做，也删去。最后可根据剩下的16列作为确定测试用例的依据。 </p>
<img src ="http://www.cnitblog.com/stomic/aggbug/51754.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-11-21 11:25 <a href="http://www.cnitblog.com/stomic/archive/2008/11/21/51754.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>测试用例设计白皮书－－场景设计方法</title><link>http://www.cnitblog.com/stomic/archive/2008/11/21/51753.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Fri, 21 Nov 2008 03:24:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/11/21/51753.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/51753.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/11/21/51753.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/51753.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/51753.html</trackback:ping><description><![CDATA[<strong>一.方法简介</strong>
<p>　　现在的软件几乎都是用事件触发来控制流程的，事件触发时的情景便形成了场景，而同一事件不同的触发顺序和处理结果就形成事件流。这种在软件设计方面的思想也可以引入到<a onclick="javascript:tagshow(event, '%C8%ED%BC%FE%B2%E2%CA%D4');" href="javascript:;" target=_self><u><strong><font color=#202859>软件测试</font></strong></u></a>中，可以比较生动地描绘出事件触发时的情景，有利于测试设计者设计<a onclick="javascript:tagshow(event, '%B2%E2%CA%D4%D3%C3%C0%FD');" href="javascript:;" target=_self><u><strong><font color=#202859>测试用例</font></strong></u></a>，同时使测试用例更容易理解和执行。</p>
<p>　　基本流和备选流：如下图所示，图中经过用例的每条路径都用基本流和备选流来表示，直黑线表示基本流，是经过用例的最简单的路径。备选流用不同的色彩表示，一个备选流可能从基本流开始，在某个特定条件下执行，然后重新加入基本流中（如备选流1和3）；也可能起源于另一个备选流（如备选流2），或者终止用例而不再重新加入到某个流（如备选流2和4）。</p>
<p align=center><img title=点击图片可在新窗口打开 style="WIDTH: 500px; CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081119/1.jpg"></p>
<p><strong>　　二.</strong><strong>实战演习</strong><strong></strong> </p>
<p><strong>　　</strong>1. 例子描述 </p>
<p>　　下图所示是ATM例子的流程示意图。</p>
<p align=center><img title=点击图片可在新窗口打开 style="WIDTH: 500px; CURSOR: pointer" src="http://www.51testing.com/ddimg/uploadimg/20081119/2.jpg"></p>
<p>　　2.场景设计：下表所示是生成的场景。</p>
<p align=center>表3-8 场景设计</p>
<table cellSpacing=0 cellPadding=0 align=center border=1>
    <tbody>
        <tr>
            <td width=277>
            <p align=left><font size=2>场景1——成功提款</font></p>
            </td>
            <td width=72>
            <p align=left><font size=2>基本流</font></p>
            </td>
            <td vAlign=top width=84>
            <p align=left><font size=2>&nbsp; </font></p>
            </td>
        </tr>
        <tr>
            <td width=277>
            <p align=left><font size=2>场景2——ATM内没有现金</font></p>
            </td>
            <td width=72>
            <p align=left><font size=2>基本流</font></p>
            </td>
            <td width=84>
            <p align=left><font size=2>备选流2</font></p>
            </td>
        </tr>
        <tr>
            <td width=277>
            <p align=left><font size=2>场景3——ATM内现金不足</font></p>
            </td>
            <td width=72>
            <p align=left><font size=2>基本流</font></p>
            </td>
            <td width=84>
            <p align=left><font size=2>备选流3</font></p>
            </td>
        </tr>
        <tr>
            <td width=277>
            <p align=left><font size=2>场景4——PIN有误（还有输入机会）</font></p>
            </td>
            <td width=72>
            <p align=left><font size=2>基本流</font></p>
            </td>
            <td width=84>
            <p align=left><font size=2>备选流4</font></p>
            </td>
        </tr>
        <tr>
            <td width=277>
            <p align=left><font size=2>场景5——PIN有误（不再有输入机会）</font></p>
            </td>
            <td width=72>
            <p align=left><font size=2>基本流</font></p>
            </td>
            <td width=84>
            <p align=left><font size=2>备选流4</font></p>
            </td>
        </tr>
        <tr>
            <td width=277>
            <p align=left><font size=2>场景6——账户不存在/账户类型有误</font></p>
            </td>
            <td width=72>
            <p align=left><font size=2>基本流</font></p>
            </td>
            <td width=84>
            <p align=left><font size=2>备选流5</font></p>
            </td>
        </tr>
        <tr>
            <td width=277>
            <p align=left><font size=2>场景7——账户余额不足</font></p>
            </td>
            <td width=72>
            <p align=left><font size=2>基本流</font></p>
            </td>
            <td width=84>
            <p align=left><font size=2>备选流6</font></p>
            </td>
        </tr>
    </tbody>
</table>
<p>　　注：为方便起见，备选流3和6（场景3和7）内的循环以及循环组合未纳入上表。</p>
<strong>3.用例设计</strong>
<p>　　对于这7个场景中的每一个场景都需要确定测试用例。可以采用矩阵或决策表来确定和管理测试用例。下面显示了一种通用格式，其中各行代表各个测试用例，而各列则代表测试用例的信息。本示例中，对于每个测试用例，存在一个测试用例ID、条件（或说明）、测试用例中涉及的所有数据元素（作为输入或已经存在于数据库中）以及预期结果。</p>
表3-9 测试用例表
<p>&#160;</p>
<table cellSpacing=0 cellPadding=0 width=560 align=center border=1>
    <tbody>
        <tr>
            <td width=70>
            <p align=center><font size=2>TC（测试用例）ID号</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景/条件</font></p>
            </td>
            <td width=44>
            <p align=center><font size=2>PIN</font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>账号</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>输入（或选择）的金额</font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>账面金额</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>ATM内的金额</font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>预期结果</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW1</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景1：成功提款</font></p>
            </td>
            <td width=44>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>成功提款</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW2</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景2：ATM内没有现金</font></p>
            </td>
            <td width=44>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>I</font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>提款选项不可用，用例结束</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW3 </font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景3：ATM内现金不足</font></p>
            </td>
            <td width=44>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>I</font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>警告消息，返回基本流步骤6，输入金额</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW4</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景4：PIN有误（还有不止一次输入机会）</font></p>
            </td>
            <td width=44>
            <p align=center><font size=2>I </font></p>
            <p align=center></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>n/a </font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V </font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>V </font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>警告消息，返回基本流步骤 4，输入 PIN</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW5</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景4：PIN有误（还有一次输入机会）</font></p>
            </td>
            <td width=44>
            <p align=center><font size=2>I </font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>n/a </font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V </font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>V </font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>警告消息，返回基本流步骤 4，输入 PIN</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW6</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景4：PIN有误（不再有输入机会）</font></p>
            </td>
            <td width=44>
            <p align=center><font size=2>I</font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>n/a</font></p>
            </td>
            <td width=46>
            <p align=center><font size=2>V</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>V </font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>警告消息，卡予保留，用例结束</font></p>
            </td>
        </tr>
    </tbody>
</table>
<p>&#160;</p>
　　<strong>4.数据设计</strong>
<p>　　一旦确定了所有的测试用例，则应对这些用例进行复审和验证以确保其准确且适度，并取消多余或等效的测试用例。</p>
<p>　　测试用例一经认可，就可以确定实际数据值（在测试用例实施矩阵中）并且设定测试数据，如表3-10所示。</p>
　　表3-10 测试用例表
<p>&#160;</p>
<table cellSpacing=0 cellPadding=0 width=560 align=center border=1>
    <tbody>
        <tr>
            <td width=70>
            <p align=center><font size=2>TC（测试用例）ID号</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景/条件</font></p>
            </td>
            <td width=43>
            <p align=center><font size=2>PIN</font></p>
            </td>
            <td width=48>
            <p align=center><font size=2>账号</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>输入（或选择）的金额</font><font size=2>（元）</font></p>
            </td>
            <td width=47>
            <p align=center><font size=2>账面<br>金额（元）</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>ATM内的金额（元）</font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>预期结果</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW1</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景1：成功提款</font></p>
            </td>
            <td width=43>
            <p align=center><font size=2>4987</font></p>
            </td>
            <td width=48>
            <p align=center><font size=2>809-498</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>50.00</font></p>
            </td>
            <td width=47>
            <p align=center><font size=2>500.0</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>2 000</font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>成功提款。账户余额被更新为450.00</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW2</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景2：ATM内没有现金</font></p>
            </td>
            <td width=43>
            <p align=center><font size=2>4987</font></p>
            </td>
            <td width=48>
            <p align=center><font size=2>809-498</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>100.00</font></p>
            </td>
            <td width=47>
            <p align=center><font size=2>500.0</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>0.00</font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>提款选项不可用，用例结束</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW3</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景3：ATM内现金不足</font></p>
            </td>
            <td width=43>
            <p align=center><font size=2>4987</font></p>
            </td>
            <td width=48>
            <p align=center><font size=2>809-498</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>100.00</font></p>
            </td>
            <td width=47>
            <p align=center><font size=2>500.0</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>70.00</font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>警告消息，返回基本流步骤6，输入金额</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW4</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景4：PIN有误（还有不止一次输入机会）</font></p>
            </td>
            <td width=43>
            <p align=center><font size=2>4978</font></p>
            </td>
            <td width=48>
            <p align=center><font size=2>809-498</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>n/a</font></p>
            </td>
            <td width=47>
            <p align=center><font size=2>500.00</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>2 000</font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>警告消息，返回基本流步骤4，输入PIN</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW5</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景4：PIN有误（还有一次输入机会）</font></p>
            </td>
            <td width=43>
            <p align=center><font size=2>4978</font></p>
            </td>
            <td width=48>
            <p align=center><font size=2>809-498</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>n/a</font></p>
            </td>
            <td width=47>
            <p align=center><font size=2>500.00</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>2 000</font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>警告消息，返回基本流步骤4，输入PIN</font></p>
            </td>
        </tr>
        <tr>
            <td width=70>
            <p align=center><font size=2>CW6</font></p>
            </td>
            <td width=123>
            <p align=center><font size=2>场景4：PIN有误（不再有输入机会）</font></p>
            </td>
            <td width=43>
            <p align=center><font size=2>4978</font></p>
            </td>
            <td width=48>
            <p align=center><font size=2>809-498</font></p>
            </td>
            <td width=67>
            <p align=center><font size=2>n/a</font></p>
            </td>
            <td width=47>
            <p align=center><font size=2>500.00</font></p>
            </td>
            <td width=57>
            <p align=center><font size=2>2 000</font></p>
            </td>
            <td width=105>
            <p align=center><font size=2>警告消息，卡予保留，用例结束</font></p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.cnitblog.com/stomic/aggbug/51753.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-11-21 11:24 <a href="http://www.cnitblog.com/stomic/archive/2008/11/21/51753.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>（转）如何解决LoadRunner监控Windows资源拒绝访问的错误 </title><link>http://www.cnitblog.com/stomic/archive/2008/11/05/51107.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Wed, 05 Nov 2008 02:39:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/11/05/51107.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/51107.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/11/05/51107.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/51107.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/51107.html</trackback:ping><description><![CDATA[1 确保监视的机器"防火墙关闭"；<br>2 确保在目标机器"服务"中开启"<font face="Courier New">Remote Procedure Call (RPC)"<br><img height=610 alt="" src="http://images.cnblogs.com/cnblogs_com/mayingbao/windows001.gif" width=976 border=0><br><br></font>3 确保在目标机器"服务"中开启"<font face="Courier New">Remote Registry"<br><img height=594 alt="" src="http://images.cnblogs.com/cnblogs_com/mayingbao/windows002.gif" width=920 border=0><br></font>注意事项：注意上述服务的"依存关系"，需优先开启其它服务，此服务才可以开启；<br>４以在"运行"下访问目标机器输入如下图所示：<img height=174 alt="" src="http://images.cnblogs.com/cnblogs_com/mayingbao/windows02.gif" width=364 border=0><br>5 管理员身份访问目标机器如下图所示：<br><img alt="" src="http://images.cnblogs.com/cnblogs_com/mayingbao/windows03.gif" border=0><br><img height=601 alt="" src="http://images.cnblogs.com/cnblogs_com/mayingbao/windows04.gif" width=797 border=0><br>经过以上操作，就可以实现监控Windows机器资源^_^
<img src ="http://www.cnitblog.com/stomic/aggbug/51107.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-11-05 10:39 <a href="http://www.cnitblog.com/stomic/archive/2008/11/05/51107.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>How to modify the subject line of automatic e-mails for TestDirector 8.0 ？</title><link>http://www.cnitblog.com/stomic/archive/2008/08/19/48142.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Tue, 19 Aug 2008 03:43:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/08/19/48142.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/48142.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/08/19/48142.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/48142.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/48142.html</trackback:ping><description><![CDATA[The subject line of automatic e-mail is constructed from the description of the defect that it is sending. In some cases, users would need to change that subject line because it might not suit their needs or some special characters cannot be displayed properly if they appear in the defect's description. Can the subject line be modified through TestDirector? <br>Notes:<br>&#8226;&nbsp;&nbsp;The following solution does not work for manual mail. <br><br>Solution: Add a section to mercury.ini that will allow customization of Auto Mail <br>You can add a section to the mercury.ini file on the TestDirector server that will allow you to define the subject line of any automatic e-mail on a per project basis. <br>1. Click Start -&gt; Run, type in mercury.ini and click &lt;OK&gt;. This will open the mercury.ini file in Notepad.<br>2. At the end of the file, add a section headed [SAQFORMAT]. Under that section you can define the subject line for each project using the following format: <br>&nbsp; &nbsp;Project Name = Subject line <br>Example:<br>[SAQFORMAT]<br>TestDirector_Demo = This mail was sent from the TestDirector_Demo project <br>This entry will cause the automatic e-mails coming from this project to have the subject line "This mail came from the TestDirector_Demo project." <br>To further customize the subject line you can use the following parameters: <br>&#8226;&nbsp; &nbsp; &nbsp; &nbsp; ?BG_BUG_ID - will be replaced by the Bug ID. <br>&#8226;&nbsp; &nbsp; &nbsp; &nbsp; ?DB - will be replaced by the database name, which is the name of the TestDirector project. <br>&#8226;&nbsp; &nbsp; &nbsp; &nbsp; ?BG_PROJECT - will be replaced by the project name. This refers to the defect's "Project" field that is taken from the user-defined list of projects. <br>&#8226;&nbsp; &nbsp; &nbsp; &nbsp; ?SERVER - will be replaced by the server name.<br>Note: You can include other fields in the subject line by adding parameters with the following format:<br>?FIELDNAME (must be uppercase) <br>Example:<br>[SAQFORMAT]<br>TestDirector_Demo = Defect #?BG_BUG_ID from ?BG_PROJECT has been updated. Please log in to ?DB on ?Server <br>The subject line of any automatic mail sent from TestDirector_Demo will read: <br>"Defect #&lt;Defect ID&gt; from &lt;Project Name&gt; has been updated. Please log in to TestDirector_Demo on &lt;Server Name&gt;" <br>The parts in the &lt;&gt; brackets are dependant on the defect being sent and the name of the TestDirector server. <br><br>NOTE A restart of the TestDirector Server is required before the new settings will take effect. This can be accomplished by right clicking on the TestDirector Icon in the System Tray and selecting Stop TestDirector. To restart the server, right click on the same icon and select Start TestDirector. If the TestDirector icon is not visible in the system tray, please refer to Problem ID 22706 - TestDirector icon does not appear in the system tray when using terminal services.
<img src ="http://www.cnitblog.com/stomic/aggbug/48142.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-08-19 11:43 <a href="http://www.cnitblog.com/stomic/archive/2008/08/19/48142.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>TD8.0发邮件乱码问题</title><link>http://www.cnitblog.com/stomic/archive/2008/08/19/48140.html</link><dc:creator>大话人生</dc:creator><author>大话人生</author><pubDate>Tue, 19 Aug 2008 03:23:00 GMT</pubDate><guid>http://www.cnitblog.com/stomic/archive/2008/08/19/48140.html</guid><wfw:comment>http://www.cnitblog.com/stomic/comments/48140.html</wfw:comment><comments>http://www.cnitblog.com/stomic/archive/2008/08/19/48140.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.cnitblog.com/stomic/comments/commentRss/48140.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/stomic/services/trackbacks/48140.html</trackback:ping><description><![CDATA[方法一：<br>客户端由到ＴＤ发送邮件一定要用UTF－８的码码格式打开，否则中文显示为乱码，原因是因为TD发送到邮件服务器是使用的UTF-16的字符集方式(通过抓包工具发现的),所以只需要修改ＴＤ服务器的<br>c:\Program Files\Common Files\Mercury Interactive\DomsInfo\StyleSheets目录下的BUG_HTML.xsl文件(假如你设置的邮件发送为html而不是text的话).文件即可，将文件中的<br>&lt;xsl:attribute name="CONTENT"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:value-of select="<a>//@td_lang"/&gt;&lt;/xsl:attribute</a>&gt;<br>修改为<br>&lt;xsl:attribute name="CONTENT"&gt;text/html;CHARSET=UTF-8&lt;/xsl:attribute&gt;<br>即可,这样以后客户端在收邮件时打开的为ＵＴＦ-８格式的文件，中文就不会是乱码了。<br>方法二：<br>改变信件的编码方式<br>td7.6&nbsp;&nbsp; sp4&nbsp;&nbsp; 的编码方式是简体中文gb2312&nbsp;&nbsp; <br>&nbsp; td7.6&nbsp;&nbsp; sp4以下的为utf－8
<img src ="http://www.cnitblog.com/stomic/aggbug/48140.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/stomic/" target="_blank">大话人生</a> 2008-08-19 11:23 <a href="http://www.cnitblog.com/stomic/archive/2008/08/19/48140.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>