Guo's profile版权所有,转载须作者同意PhotosBlogLists Tools Help

Blog


    July 31

    xml schame -4

    在 XML Schema 中,我们有两种方式来定义复合元素:

    1. 通过命名此元素,可直接对"employee"元素进行声明,就像这样:

    <xs:element name="employee">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="firstname" type="xs:string"/>
          <xs:element name="lastname" type="xs:string"/>
        </xs:sequence>
      </xs:complexType>
    </xs:element>

    假如您使用上面所描述的方法,那么仅有 "employee" 可使用所规定的复合类型。请注意其子元素,"firstname" 以及 "lastname",被包围在指示器 <sequence>中。这意味着子元素必须以它们被声明的次序出现。您会在 XSD 指示器 这一节学习更多有关指示器的知识。

    2. "employee" 元素可以使用 type 属性,这个属性的作用是引用要使用的复合类型的名称:

    <xs:element name="employee" type="personinfo"/>
    
    <xs:complexType name="personinfo">
      <xs:sequence>
        <xs:element name="firstname" type="xs:string"/>
        <xs:element name="lastname" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>

    如果您使用了上面所描述的方法,那么若干元素均可以使用相同的复合类型,比如这样:

    <xs:element name="employee" type="personinfo"/>
    <xs:element name="student" type="personinfo"/>
    <xs:element name="member" type="personinfo"/>
    
    <xs:complexType name="personinfo">
      <xs:sequence>
        <xs:element name="firstname" type="xs:string"/>
        <xs:element name="lastname" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>

    您也可以在已有的复合元素之上以某个复合元素为基础,然后添加一些元素,就像这样:

    <xs:element name="employee" type="fullpersoninfo"/>
    
    <xs:complexType name="personinfo">
      <xs:sequence>
        <xs:element name="firstname" type="xs:string"/>
        <xs:element name="lastname" type="xs:string"/>
      </xs:sequence>
    </xs:complexType>
    
    <xs:complexType name="fullpersoninfo">
      <xs:complexContent>
        <xs:extension base="personinfo">
          <xs:sequence>
            <xs:element name="address" type="xs:string"/>
            <xs:element name="city" type="xs:string"/>
            <xs:element name="country" type="xs:string"/>
          </xs:sequence>
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>

    xml schame -3

    对空白字符的限定

    如需规定对空白字符(whitespace characters)的处理方式,我们需要使用 whiteSpace 限定。

    下面的例子定义了带有一个限定的名为 "address" 的元素。这个 whiteSpace 限定被设置为 "preserve",这意味着 XML 处理器不会移除任何空白字符:

    <xs:element name="address">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:whiteSpace value="preserve"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    这个例子也定义了带有一个限定的名为 "address" 的元素。这个 whiteSpace 限定被设置为 "replace",这意味着 XML 处理器将移除所有空白字符(换行、回车、空格以及制表符):

    <xs:element name="address">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:whiteSpace value="replace"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    这个例子也定义了带有一个限定的名为 "address" 的元素。这个 whiteSpace 限定被设置为 "collapse",这意味着 XML 处理器将移除所有空白字符(换行、回车、空格以及制表符会被替换为空格,开头和结尾的空格会被移除,而多个连续的空格会被缩减为一个单一的空格):

    <xs:element name="address">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:whiteSpace value="collapse"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    对长度的限定

    如需限制元素中值的长度,我们需要使用 length、maxLength 以及 minLength 限定。

    本例定义了带有一个限定且名为 "password" 的元素。其值必须精确到 8 个字符:

    <xs:element name="password">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:length value="8"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    这个例子也定义了带有一个限定的名为 "password" 的元素。其值最小为 5 个字符,最大为 8 个字符:

    <xs:element name="password">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:minLength value="5"/>
        <xs:maxLength value="8"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    数据类型的限定

    限定
    描述

    enumeration
    定义可接受值的一个列表

    fractionDigits
    定义所允许的最大的小数位数。必须大于等于0。

    length
    定义所允许的字符或者列表项目的精确数目。必须大于或等于0。

    maxExclusive
    定义数值的上限。所允许的值必须小于此值。

    maxInclusive
    定义数值的上限。所允许的值必须小于或等于此值。

    maxLength
    定义所允许的字符或者列表项目的最大数目。必须大于或等于0。

    minExclusive
    定义数值的下限。所允许的值必需大于此值。

    minInclusive
    定义数值的下限。所允许的值必需大于或等于此值。

    minLength
    定义所允许的字符或者列表项目的最小数目。必须大于或等于0。

    pattern
    定义可接受的字符的精确序列。

    totalDigits
    定义所允许的阿拉伯数字的精确位数。必须大于0。

    whiteSpace
    定义空白字符(换行、回车、空格以及制表符)的处理方式。

    xml schema 2

    对一系列值的限定

    如需把 XML 元素的内容限制定义为一系列可使用的数字或字母,我们要使用模式约束(pattern constraint)。

    下面的例子定义了带有一个限定的名为 "letter" 的元素。可接受的值只有小写字母 a - z 其中的一个:

    <xs:element name="letter">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:pattern value="[a-z]"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

     

     

    对一组值的限定

    如需把 XML 元素的内容限制为一组可接受的值,我们要使用枚举约束(enumeration constraint)。

    下面的例子定义了带有一个限定的名为 "car" 的元素。可接受的值只有:Audi, Golf, BMW:

    <xs:element name="car">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:enumeration value="Audi"/>
        <xs:enumeration value="Golf"/>
        <xs:enumeration value="BMW"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 
     

    对值的限定

    下面的例子定义了带有一个限定且名为 "age" 的元素。age 的值不能低于 0 或者高于 120:

    <xs:element name="age">
    
    <xs:simpleType>
      <xs:restriction base="xs:integer">
        <xs:minInclusive value="0"/>
        <xs:maxInclusive value="120"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

     

     

    下一个例子定义了带有一个限定的名为 "initials" 的元素。可接受的值是大写字母 A - Z 其中的三个:

    <xs:element name="initials">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:pattern value="[A-Z][A-Z][A-Z]"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    下一个例子也定义了带有一个限定的名为 "initials" 的元素。可接受的值是大写或小写字母 a - z 其中的三个:

    <xs:element name="initials">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:pattern value="[a-zA-Z][a-zA-Z][a-zA-Z]"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    下一个例子定义了带有一个限定的名为 "choice 的元素。可接受的值是字母 x, y 或 z 中的一个:

    <xs:element name="choice">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:pattern value="[xyz]"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    下一个例子定义了带有一个限定的名为 "prodid" 的元素。可接受的值是五个阿拉伯数字的一个序列,且每个数字的范围是 0-9:

    <xs:element name="prodid">
    
    <xs:simpleType>
      <xs:restriction base="xs:integer">
        <xs:pattern value="[0-9][0-9][0-9][0-9][0-9]"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

     

     

    对一系列值的其他限定

    下面的例子定义了带有一个限定的名为 "letter" 的元素。可接受的值是 a - z 中零个或多个字母:

    <xs:element name="letter">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:pattern value="([a-z])*"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    下面的例子定义了带有一个限定的名为 "letter" 的元素。可接受的值是一对或多对字母,每对字母由一个小写字母后跟一个大写字母组成。举个例子,"sToP"将会通过这种模式的验证,但是 "Stop"、"STOP" 或者 "stop" 无法通过验证:

    <xs:element name="letter">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:pattern value="([a-z][A-Z])+"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    下面的例子定义了带有一个限定的名为 "gender" 的元素。可接受的值是 male 或者 female:

    <xs:element name="gender">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:pattern value="male|female"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    下面的例子定义了带有一个限定的名为 "password" 的元素。可接受的值是由 8 个字符组成的一行字符,这些字符必须是大写或小写字母 a - z 亦或数字 0 - 9:

    <xs:element name="password">
    
    <xs:simpleType>
      <xs:restriction base="xs:string">
        <xs:pattern value="[a-zA-Z0-9]{8}"/>
      </xs:restriction>
    </xs:simpleType>
    
    </xs:element> 

    XML Schema

    XML Schema 支持数据类型

    XML Schema 最重要的能力之一就是对数据类型的支持。

    通过对数据类型的支持:
    • 可更容易地描述允许的文档内容
    • 可更容易地验证数据的正确性
    • 可更容易地与来自数据库的数据一并工作
    • 可更容易地定义数据约束(data facets)
    • 可更容易地定义数据模型(或称数据格式)
    • 可更容易地在不同的数据类型间转换数据

     

     

    facet
    fac·et Audio Help /ˈfæsɪt/ Pronunciation Key - Show Spelled Pronunciation[fas-it] Pronunciation Key - Show IPA Pronunciation noun, verb, -et·ed, -et·ing or (especially British) -et·ted, -et·ting.
    n.
    (多面体的)面, (宝石等的)刻面, 小平面, 方面, 琢面
    vt.
    在...上刻画

     

    我们把符合 XML 语法的文档称为形式良好的 XML 文档,比如:

    • 它必须以 XML 声明开头
    • 它必须拥有唯一的根元素
    • 开始标签必须与结束标签相匹配
    • 元素对大小写敏感
    • 所有的元素都必须关闭
    • 所有的元素都必须正确地嵌套
    • 必须对特殊字符使用实体

    July 30

    EasyMock 简介

    http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/index.html

    手动的构造 Mock 对象会给开发人员带来额外的编码量,而且这些为创建 Mock 对象而编写的代码很有可能引入错误。目前,有许多开源项目对动态构建 Mock 对象提供了支持,这些项目能够根据现有的接口或类动态生成,这样不仅能避免额外的编码工作,同时也降低了引入错误的可能。

    EasyMock 是一套用于通过简单的方法对于给定的接口生成 Mock 对象的类库。它提供对接口的模拟,能够通过录制、回放、检查三步来完成大体的测试过程,可以验证方法的调用种类、次数、顺序,可以令 Mock 对象返回指定的值或抛出指定异常。通过 EasyMock,我们可以方便的构造 Mock 对象从而使单元测试顺利进行。

     

    如果您要模拟的是一个具体类而非接口,那么您需要下载扩展包 EasyMock Class Extension 2.2.2。在对具体类进行模拟时,您只要用 org.easymock.classextension.EasyMock 类中的静态方法代替 org.easymock.EasyMock 类中的静态方法即可

    July 29

    JUnit Assert方法总结

    junit中的assert方法全部放在Assert类中,总结一下junit类中assert方法的分类。
    1.assertTrue/False([String message,]boolean condition);
        判断一个条件是true还是false。感觉这个最好用了,不用记下来那么多的方法名。
    2.fail([String message,]);
        失败,可以有消息,也可以没有消息。
    3.assertEquals([String message,]Object expected,Object actual);
        判断是否相等,可以指定输出错误信息。
        第一个参数是期望值,第二个参数是实际的值。
        这个方法对各个变量有多种实现。在JDK1.5中基本一样。
        但是需要主意的是float和double最后面多一个delta的值,可能是误差范围,不确定这个    单词什么意思,汗一个。
    4.assertNotNull/Null([String message,]Object obj);
        判读一个对象是否非空(非空)。
    5.assertSame/NotSame([String message,]Object expected,Object actual);
        判断两个对象是否指向同一个对象。看内存地址。
    7.failNotSame/failNotEquals(String message, Object expected, Object actual)
        当不指向同一个内存地址或者不相等的时候,输出错误信息。
        注意信息是必须的,而且这个输出是格式化过的。

    JUnit 4 中的 assertEquals 现在支持数组

     
    @Test
    public void verifyArrayContents() throws Exception{
    String[] actual = new String[] {"JUnit 3.8.x", "JUnit 4", "TestNG"};
    String[] var = new String[] {"JUnit 3.8.x", "JUnit 4.1", "TestNG 5.5"};
    assertEquals("the two arrays should not be equal", actual, var);       
    }

    参数测试

    偶尔,应用程序的业务逻辑要求您编写许多不定量的测试来保证其健壮。在 JUnit 之前的版本中,这种场景很不方便,主要是因为一个测试中方法的参数组各不相同,意味着要为每一个单独的组编写一个测试用例。

    JUnit 4 引入了一项卓越的新功能,即能够创建由参数值供给的通用测试。结果是,您可以创建一个单个的测试用例并多次运行 —— 为您创建的每个参数运行一次。

    两个新注释

    在 JUnit 4 中,套件语义被两个新注释所替代。第一个是 @RunWith,设计它是为了方便让不同的运行器(除了构建进框架的运行器)执行一个特别的测试类。JUnit 4 绑定一个叫做 Suite 的套件运行器,必须在 @RunWith 注释中指定这个运行器。不仅如此,还必须提供另一项叫做 @SuiteClasses 的注释,它将一个意欲表示测试套件的类列表作为参数。

    import org.junit.runner.RunWith;
    import org.junit.runners.Suite;
    import org.junit.runners.Suite.SuiteClasses;

    @RunWith(Suite.class)
    @SuiteClasses({ParametricRegularExpressionTest.class,
          RegularExpressionTest.class,
          TimedRegularExpressionTest.class})
    public class JUnit4Suite {

    }

    如果只想运行一次固件会怎样呢?与其实现一个旧式的装饰器,如清单 9 所示,您不如使用 @BeforeClass 注释,如清单 11 所

    public class RegularExpressionJUnit4Test {
    private static String zipRegEx = "^\\d{5}([\\-]\\d{4})?$";
    private static Pattern pattern;

    @BeforeClass
    public static void setUpBeforeClass() throws Exception {
      pattern = Pattern.compile(zipRegEx);
    }

    @Test
    public void verifyZipCodeNoMatch() throws Exception{       
      Matcher mtcher = this.pattern.matcher("2211");
      boolean notValid = mtcher.matches();       
      assertFalse("Pattern did validate zip code", notValid);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void verifyZipCodeGroupException() throws Exception{       
      Matcher mtcher = this.pattern.matcher("22101-5051");
      boolean isValid = mtcher.matches();           
      mtcher.group(2);       
    }
    }

    @Before

    清单 10 中的测试用例包括一个使用 @Before 注释的固件,该固件针对两个测试运行

    import java.util.regex.Matcher;
    import java.util.regex.Pattern;

    import org.junit.BeforeClass;
    import org.junit.Test;
    import static org.junit.Assert.assertTrue;
    import static org.junit.Assert.assertFalse;

    public class RegularExpressionJUnit4Test {
    private static String zipRegEx = "^\\d{5}([\\-]\\d{4})?$";
    private static Pattern pattern;

    @Before
    public static void setUpBeforeClass() throws Exception {
      pattern = Pattern.compile(zipRegEx);
    }

    @Test
    public void verifyZipCodeNoMatch() throws Exception{       
      Matcher mtcher = this.pattern.matcher("2211");
      boolean notValid = mtcher.matches();       
      assertFalse("Pattern did validate zip code", notValid);
    }

    @Test(expected=IndexOutOfBoundsException.class)
    public void verifyZipCodeGroupException() throws Exception{       
      Matcher mtcher = this.pattern.matcher("22101-5051");
      boolean isValid = mtcher.matches();           
      mtcher.group(2);       
    }
    }

    Junit 4

    异常测试

    @Test(expected=IndexOutOfBoundsException.class)

    超时测试

    @Test(timeout=1)

    忽略测试

    @Ignore("this regular expression isn't working yet")

    @Test

    public void verifyZipCodeMatch() throws Exception{

    }

     ignoretest[1]

    July 25

    selenium

    selenium 验证返回的字符,之前一定要等一下

    July 24

    JUnit 4 抢先看

    JUnit 由 Kent Beck 和 Erich Gamma 开发,几乎毫无疑问是迄今所开发的最重要的第三方 Java 库。正如 Martin Fowler 所说,“在软件开发领域,从来就没有如此少的代码起到了如此重要的作用”。JUnit 引导并促进了测试的盛行。由于 JUnit,Java 代码变得更健壮,更可靠,bug 也比以前更少。JUnit(它本身的灵感来自 Smalltalk 的 SUnit)衍生了许多 xUnit 工具,将单元测试的优势应用于各种语言。nUnit (.NET)、pyUnit (Python)、CppUnit (C++)、dUnit (Delphi) 以及其他工具,影响了各种平台和语言上的程序员的测试工作。

    然而,JUnit 仅仅是一个工具而已。真正的优势来自于 JUnit 所采用的思想和技术,而不是框架本身。单元测试、测试先行的编程和测试驱动的开发并非都要在 JUnit 中实现,任何比较 GUI 的编程都必须用 Swing 来完成。JUnit 本身的最后一次更新差不多是三年以前了。尽管它被证明比大多数框架更健壮、更持久,但是也发现了 bug;而更重要的是,Java 不断在发展。Java 语言现在支持泛型、枚举、可变长度参数列表和注释,这些特性为可重用的框架设计带来了新的可能。

    JUnit 的停滞不前并没有被那些想要废弃它的程序员所打败。挑战者包括 Bill Venners 的 Artima SuiteRunner 以及 Cedric Beust 的 TestNG 等。这些库有一些可圈可点的特性,但是都没有达到 JUnit 的知名度和市场占有份额。它们都没有在诸如 Ant、Maven 或 Eclipse 之类的产品中具有广泛的开箱即用支持。所以 Beck 和 Gamma 着手开发了一个新版本的 JUnit,它利用 Java 5 的新特性(尤其是注释)的优势,使得单元测试比起用最初的 JUnit 来说更加简单。用 Beck 的话来说,“JUnit 4 的主题是通过进一步简化 JUnit,鼓励更多的开发人员编写更多的测试。”JUnit 4 尽管保持了与现有 JUnit 3.8 测试套件的向后兼容,但是它仍然承诺是自 JUnit 1.0 以来 Java 单元测试方面最重大的改进。

    注意:该框架的改进是相当前沿的。尽管 JUnit 4 的大轮廓很清晰,但是其细节仍然可以改变。这意味着本文是对 JUnit 4 抢先看,而不是它的最终效果。

    测试方法

    以前所有版本的 JUnit 都使用命名约定和反射来定位测试。例如,下面的代码测试 1+1 等于 2:

    import junit.framework.TestCase;
    public class AdditionTest extends TestCase {
      private int x = 1;
      private int y = 1;
      
      public void testAddition() {
        int z = x + y;
        assertEquals(2, z);
      }
    }

    而在 JUnit 4 中,测试是由 @Test 注释来识别的,如下所示:

    import org.junit.Test;
    import junit.framework.TestCase;
    public class AdditionTest extends TestCase {
      private int x = 1;
      private int y = 1;
      
      @Test public void testAddition() {
        int z = x + y;
        assertEquals(2, z);
      }
    }

    使用注释的优点是不再需要将所有的方法命名为 testFoo()testBar(),等等。例如,下面的方法也可以工作:

    import org.junit.Test;
    import junit.framework.TestCase;
    public class AdditionTest extends TestCase {
      private int x = 1;
      private int y = 1;
      
      @Test public void additionTest() {
        int z = x + y;
        assertEquals(2, z);
      }
    }

    下面这个方法也同样能够工作:

    import org.junit.Test;
    import junit.framework.TestCase;
    public class AdditionTest extends TestCase {
      private int x = 1;
      private int y = 1;
      
      @Test public void addition() {
        int z = x + y;
        assertEquals(2, z);
      }
    }

    这允许您遵循最适合您的应用程序的命名约定。例如,我介绍的一些例子采用的约定是,测试类对其测试方法使用与被测试的类相同的名称。例如,List.contains()ListTest.contains() 测试,List.add()ListTest.addAll() 测试,等等。

    TestCase 类仍然可以工作,但是您不再需要扩展它了。只要您用 @Test 来注释测试方法,就可以将测试方法放到任何类中。但是您需要导入 junit.Assert 类以访问各种 assert 方法,如下所示:

    import org.junit.Assert;
    public class AdditionTest {
      private int x = 1;
      private int y = 1;
      
      @Test public void addition() {
        int z = x + y;
        Assert.assertEquals(2, z);
      }
    }

    您也可以使用 JDK 5 中新特性(static import),使得与以前版本一样简单:

    import static org.junit.Assert.assertEquals;
    public class AdditionTest {
      private int x = 1;
      private int y = 1;
      
      @Test public void addition() {
        int z = x + y;
        assertEquals(2, z);
      }
    }

    这种方法使得测试受保护的方法非常容易,因为测试案例类现在可以扩展包含受保护方法的类了。



    回页首

    SetUp 和 TearDown

    JUnit 3 测试运行程序(test runner)会在运行每个测试之前自动调用 setUp() 方法。该方法一般会初始化字段,打开日志记录,重置环境变量,等等。例如,下面是摘自 XOM 的 XSLTransformTest 中的 setUp() 方法:

    protected void setUp() {
            
        System.setErr(new PrintStream(new ByteArrayOutputStream()));
            
        inputDir = new File("data");
        inputDir = new File(inputDir, "xslt");
        inputDir = new File(inputDir, "input");
            
    }

    在 JUnit 4 中,您仍然可以在每个测试方法运行之前初始化字段和配置环境。然而,完成这些操作的方法不再需要叫做 setUp(),只要用 @Before 注释来指示即可,如下所示:

    @Before protected void initialize() {
            
        System.setErr(new PrintStream(new ByteArrayOutputStream()));
            
        inputDir = new File("data");
        inputDir = new File(inputDir, "xslt");
        inputDir = new File(inputDir, "input");
            
    }

    甚至可以用 @Before 来注释多个方法,这些方法都在每个测试之前运行:

    @Before protected void findTestDataDirectory() {
        inputDir = new File("data");
        inputDir = new File(inputDir, "xslt");
        inputDir = new File(inputDir, "input");
    }
        
     @Before protected void redirectStderr() {
        System.setErr(new PrintStream(new ByteArrayOutputStream()));
    }

    清除方法与此类似。在 JUnit 3 中,您使用 tearDown() 方法,该方法类似于我在 XOM 中为消耗大量内存的测试所使用的方法:

    protected void tearDown() {
      doc = null;
      System.gc();   
    } 

    对于 JUnit 4,我可以给它取一个更自然的名称,并用 @After 注释它:

    @After protected void disposeDocument() {
      doc = null;
      System.gc();   
    } 

    @Before 一样,也可以用 @After 来注释多个清除方法,这些方法都在每个测试之后运行。

    最后,您不再需要在超类中显式调用初始化和清除方法,只要它们不被覆盖即可,测试运行程序将根据需要自动为您调用这些方法。超类中的 @Before 方法在子类中的 @Before 方法之前被调用(这反映了构造函数调用的顺序)。@After 方法以反方向运行:子类中的方法在超类中的方法之前被调用。否则,多个 @Before@After 方法的相对顺序就得不到保证。

    套件范围的初始化

    JUnit 4 也引入了一个 JUnit 3 中没有的新特性:类范围的 setUp()tearDown() 方法。任何用 @BeforeClass 注释的方法都将在该类中的测试方法运行之前刚好运行一次,而任何用 @AfterClass 注释的方法都将在该类中的所有测试都运行之后刚好运行一次。

    例如,假设类中的每个测试都使用一个数据库连接、一个网络连接、一个非常大的数据结构,或者还有一些对于初始化和事情安排来说比较昂贵的其他资源。不要在每个测试之前都重新创建它,您可以创建它一次,并还原它一次。该方法将使得有些测试案例运行起来快得多。例如,当我测试调用第三方库的代码中的错误处理时,我通常喜欢在测试开始之前重定向 System.err,以便输出不被预期的错误消息打乱。然后我在测试结束后还原它,如下所示:

    // This class tests a lot of error conditions, which
    // Xalan annoyingly logs to System.err. This hides System.err 
    // before each test and restores it after each test.
    private PrintStream systemErr;
        
    @BeforeClass protected void redirectStderr() {
        systemErr = System.err; // Hold on to the original value
        System.setErr(new PrintStream(new ByteArrayOutputStream()));
    }
        
    @AfterClass protected void tearDown() {
        // restore the original value
        System.setErr(systemErr);
    }

    没有必要在每个测试之前和之后都这样做。但是一定要小心对待这个特性。它有可能会违反测试的独立性,并引入非预期的混乱。如果一个测试在某种程度上改变了 @BeforeClass 所初始化的一个对象,那么它有可能会影响其他测试的结果。它有可能在测试套件中引入顺序依赖,并隐藏 bug。与任何优化一样,只在剖析和基准测试证明您具有实际的问题之后才实现这一点。这就是说,我看到了不止一个测试套件运行时间如此之长,以至不能像它所需要的那样经常运行,尤其是那些需要建立很多网络和数据库连接的测试。(例如,LimeWire 测试套件运行时间超过两小时。)要加快这些测试套件,以便程序员可以更加经常地运行它们,您可以做的就是减少 bug。



    回页首

    测试异常

    异常测试是 JUnit 4 中的最大改进。旧式的异常测试是在抛出异常的代码中放入 try 块,然后在 try 块的末尾加入一个 fail() 语句。例如,该方法测试被零除抛出一个 ArithmeticException

    public void testDivisionByZero() {
        
        try {
            int n = 2 / 0;
            fail("Divided by zero!");
        }
        catch (ArithmeticException success) {
            assertNotNull(success.getMessage());
        }
        
    }

    该方法不仅难看,而且试图挑战代码覆盖工具,因为不管测试是通过还是失败,总有一些代码不被执行。在 JUnit 4 中,您现在可以编写抛出异常的代码,并使用注释来声明该异常是预期的:

    @Test(expected=ArithmeticException.class) 
      public void divideByZero() {
        int n = 2 / 0;
    }

    如果该异常没有抛出(或者抛出了一个不同的异常),那么测试就将失败。但是如果您想要测试异常的详细消息或其他属性,则仍然需要使用旧式的 try-catch 样式。



    回页首

    被忽略的测试

    也许您有一个测试运行的时间非常地长。不是说这个测试应该运行得更快,而是说它所做的工作从根本上比较复杂或缓慢。需要访问远程网络服务器的测试通常都属于这一类。如果您不在做可能会中断该类测试的事情,那么您可能想要跳过运行时间长的测试方法,以缩短编译-测试-调试周期。或者也许是一个因为超出您的控制范围的原因而失败的测试。例如,W3C XInclude 测试套件测试 Java 还不支持的一些 Unicode 编码的自动识别。不必老是被迫盯住那些红色波浪线,这类测试可以被注释为 @Ignore,如下所示:

    // Java doesn't yet support 
    // the UTF-32BE and UTF32LE encodings
        @Ignore public void testUTF32BE() 
          throws ParsingException, IOException, XIncludeException {
          
            File input = new File(
              "data/xinclude/input/UTF32BE.xml"
            );
            Document doc = builder.build(input);
            Document result = XIncluder.resolve(doc);
            Document expectedResult = builder.build(
              new File(outputDir, "UTF32BE.xml")
            );
            assertEquals(expectedResult, result);
                    
        }

    测试运行程序将不运行这些测试,但是它会指出这些测试被跳过了。例如,当使用文本界面时,会输出一个“I”(代表 ignore),而不是为通过的测试输出所经历的时间,也不是为失败的测试输出“E”:

    $ java -classpath .:junit.jar org.junit.runner.JUnitCore 
      nu.xom.tests.XIncludeTest
    JUnit version 4.0rc1
    .....I..
    Time: 1.149
    OK (7 tests)

    但是一定要小心。最初编写这些测试可能有一定的原因。如果永远忽略这些测试,那么它们期望测试的代码可能会中断,并且这样的中断可能不能被检测到。忽略测试只是一个权宜之计,不是任何问题的真正解决方案。



    回页首

    时间测试

    测试性能是单元测试最为痛苦的方面之一。JUnit 4 没有完全解决这个问题,但是它对这个问题有所帮助。测试可以用一个超时参数来注释。如果测试运行的时间超过指定的毫秒数,则测试失败。例如,如果测试花费超过半秒时间去查找以前设置的一个文档中的所有元素,那么该测试失败:

    @Test(timeout=500) public void retrieveAllElementsInDocument() {
        doc.query("//*");
    } 

    除了简单的基准测试之外,时间测试也对网络测试很有用。在一个测试试图连接到的远程主机或数据库宕机或变慢时,您可以忽略该测试,以便不阻塞所有其他的测试。好的测试套件执行得足够快,以至程序员可以在每个测试发生重大变化之后运行这些测试,有可能一天运行几十次。设置一个超时使得这一点更加可行。例如,如果解析 http://www.ibiblio.org/xml 花费了超过 2 秒,那么下面的测试就会超时:

    @Test(timeout=2000) 
      public void remoteBaseRelativeResolutionWithDirectory()
       throws IOException, ParsingException {
          builder.build("http://www.ibiblio.org/xml");
      } 



    回页首

    新的断言

    JUnit 4 为比较数组添加了两个 assert() 方法:

    public static void assertEquals(Object[] expected, Object[] actual)
    public static void assertEquals(String message, Object[] expected, 
    Object[] actual)
    

    这两个方法以最直接的方式比较数组:如果数组长度相同,且每个对应的元素相同,则两个数组相等,否则不相等。数组为空的情况也作了考虑。



    回页首

    需要补充的地方

    JUnit 4 基本上是一个新框架,而不是旧框架的升级版本。JUnit 3 开发人员可能会找到一些原来没有的特性。

    最明显的删节就是 GUI 测试运行程序。如果您想在测试通过时看到赏心悦目的绿色波浪线,或者在测试失败时看到令人焦虑的红色波浪线,那么您需要一个具有集成 JUnit 支持的 IDE,比如 Eclipse。不管是 Swing 还是 AWT 测试运行程序都不会被升级或捆绑到 JUnit 4 中。

    下一个惊喜是,失败(assert 方法检测到的预期的错误)与错误(异常指出的非预期的错误)之间不再有任何差别。尽管 JUnit 3 测试运行程序仍然可以区别这些情况,而 JUnit 4 运行程序将不再能够区分。

    最后,JUnit 4 没有 suite() 方法,这些方法用于从多个测试类构建一个测试套件。相反,可变长参数列表用于允许将不确定数量的测试传递给测试运行程序。

    我对消除了 GUI 测试运行程序并不感到太高兴,但是其他更改似乎有可能增加 JUnit 的简单性。只要考虑有多少文档和 FAQ 当前专门用于解释这几点,然后考虑对于 JUnit 4,您不再需要解释这几点了。



    回页首

    编译和运行 JUnit 4

    当前,还没有 JUnit 4 的库版本。如果您想要体验新的版本,那么您需要从 SourceForge 上的 CVS 知识库获取它。分支(branch)是“Version4”(参见 参考资料)。注意,很多的文档没有升级,仍然是指以旧式的 3.x 方式做事。Java 5 对于编译 JUnit 4 是必需的,因为 JUnit 4 大量用到注释、泛型以及 Java 5 语言级的其他特性。

    自 JUnit 3 以来,从命令行运行测试的语法发生了一点变化。您现在使用 org.junit.runner.JUnitCore 类:

    $ java -classpath .:junit.jar org.junit.runner.JUnitCore 
      TestA TestB TestC...
    JUnit version 4.0rc1
    Time: 0.003
    OK (0 tests)

    兼容性

    Beck 和 Gamma 努力维持向前和向后兼容。JUnit 4 测试运行程序可以运行 JUnit 3 测试,不用做任何更改。只要将您想要运行的每个测试的全限定类名传递给测试运行程序,就像针对 JUnit 4 测试一样。运行程序足够智能,可以分辨出哪个测试类依赖于哪个版本的 JUnit,并适当地调用它。

    向后兼容要困难一些,但是也可以在 JUnit 3 测试运行程序中运行 JUnit 4 测试。这一点很重要,所以诸如 Eclipse 之类具有集成 JUnit 支持的工具可以处理 JUnit 4,而不需要更新。为了使 JUnit 4 测试可以运行在 JUnit 3 环境中,可以将它们包装在 JUnit4TestAdapter 中。将下面的方法添加到您的 JUnit 4 测试类中应该就足够了:

    public static junit.framework.Test suite() {
      return new JUnit4TestAdapter(AssertionTest.class);    
    }

    但是由于 Java 比较多变,所以 JUnit 4 一点都不向后兼容。JUnit 4 完全依赖于 Java 5 特性。对于 Java 1.4 或更早版本,它将不会编译或运行。

    selenium

    使用示例: java -jar selenium-server.jar [-interactive] [options]

    • -port <nnnn>: selenium服务器使用的端口号(默认 4444)
    • -timeout <nnnn>: 我们放弃前(超时)所等待的秒数
    • -interactive: 进入交互模式。参考教程获取更多信息
    • -multiWindow: 进入被测试网站都在单独窗口打开的模式,并且selenium支持frame
    • -forcedBrowserMode <browser>: 设置浏览器模式(例如,所有的会话都使用"*iexplore",不管给getNewBrowserSession传递什么参数)
    • -userExtensions <file>: 指定一个被载入到selenium的JavaScript文件
    • -browserSessionReuse: 停止在测试间重新初始化和替换浏览器。
    • -alwaysProxy: 默认情况下,我们尽量少的进行代理;设置这个标志将会强制所有的浏览器通讯都通过代理
    • -firefoxProfileTemplate <dir>: 一般情况,我们在每次启动之前都生成一个干净的Firefox设置。您可以指定一个目录来让我们将您的设置拷贝过来,代替我们生成的。
    • -debug: 进入debug模式,会有更多的跟踪调试信息
    • -htmlSuite <browser> <startURL> <suiteFile> <resultFile>: 使用指定的浏览器(例如"*firefox")在指定的URL(例如"http://www.google.com"),运行一个单独的HTML Selenese (Selenium Core)测试套件然后立即退出。您需要指定HTML测试套件的绝对路径还有我们将会生成的HTML测试结果文件的路径。
    • -proxyInjectionMode: 进入代理注入模式,这个模式中selenium服务器作为进入测试程序的所有内容的代理服务器。在这个模式下,可以跨多个域访问,并且还支持如下附加参数:
      • -dontInjectRegex <regex>: 附加的正则表达式,代理注入模式能够使用它决定是否进行注入
      • -userJsInjection <file>: 指定一个JavaScript文件,将它注入到所有页面中
      • -userContentTransformation <regex> <replacement>: 一个正则表达式,对所有被测HTML内容进行匹配;第二个string将会对替换所有匹配的内容。这个标志能够使用多次。一个简单的适合使用这个参数的例子:如果你添加"-userContentTransformation https http"那么测试应用程序的HTML中的所有"https"字符串都会被替换为"http"。

    Jboss selenium port conflict problem 4444

    下载Selenium RC后,解压后,进入selenium-server-0.9.2所在的目录,在命令行中启动selenium-server.jar,如果启动不成功,则可能是默认端口号的问题,可加参数 –port,后面指定一个新的端口号。

    July 23

    Selenium

    http://www.ibm.com/developerworks/cn/java/wa-selenium-ajax/使用 Selenium 和在浏览器中运行测试还有很多其他好处。下面是主要的两大好处:

    • 通过编写模仿用户操作的 Selenium 测试脚本,可以从终端用户的角度来测试应用程序。
    • 通过在不同浏览器中运行测试,更容易发现浏览器的不兼容性。

    Selenium 测试直接在浏览器中运行,就像真实用户所做的一样。Selenium 测试可以在 Windows、Linux 和 MacintoshAnd 上的 Internet Explorer、Mozilla 和 Firefox 中运行。其他测试工具都不能覆盖如此多的平台。

    blog again

    blog again, maybe this is the most convenient way to store knowledge