www.5197.com-澳门新蒲京娱乐场官网

热门关键词: www.5197.com,澳门新蒲京娱乐场官网

【java测试】Junit、Mock 代码覆盖率澳门新蒲京娱乐

看起来挺不错吧,不是吗?但还是存在一个问题:如果属性值没有设置,在setup中的setProperty返回null,而teardown会抛出一个NullPointerException。所以正确的使系统属性维持不变的单元测试应该是:Test.java

国内私募机构九鼎控股打造APP,来就送 20元现金领取地址:
内部邀请码:C8E245J **(不写邀请码,没有现金送) 国内私募机构九鼎控股打造,九鼎投资是在全国股份转让系统挂牌的公众公司,股票代码为430719,为“中国PE第一股”,市值超1000亿元。 **

JUnit4

与JUnit3不同,JUnit4通过注解的方式来识别测试方法。目前支持的主要注解有:

  • @BeforeClass 全局只会执行一次,而且是第一个运行
  • @Before 在测试方法运行之前运行
  • @Test 测试方法
  • @After 在测试方法运行之后允许
  • @AfterClass 全局只会执行一次,而且是最后一个运行
  • @Ignore 忽略此方法

下面举一个样例:

  1. import org.junit.After;  
  2. import org.junit.AfterClass;  
  3. import org.junit.Assert;  
  4. import org.junit.Before;  
  5. import org.junit.BeforeClass;  
  6. import org.junit.Ignore;  
  7. import org.junit.Test;  
  8.    
  9. public class Junit4TestCase {  
  10.    
  11.     @BeforeClass  
  12.     public static void setUpBeforeClass() {  
  13.         System.out.println("Set up before class");  
  14.     }  
  15.    
  16.     @Before  
  17.     public void setUp() throws Exception {  
  18.         System.out.println("Set up");  
  19.     }  
  20.    
  21.     @Test  
  22.     public void testMathPow() {  
  23.         System.out.println("Test Math.pow");  
  24.         Assert.assertEquals(4.0, Math.pow(2.0, 2.0), 0.0);  
  25.     }  
  26.    
  27.     @Test  
  28.     public void testMathMin() {  
  29.         System.out.println("Test Math.min");  
  30.         Assert.assertEquals(2.0, Math.min(2.0, 4.0), 0.0);  
  31.     }  
  32.    
  33.         // 期望此方法抛出NullPointerException异常  
  34.     @Test(expected = NullPointerException.class)  
  35.     public void testException() {  
  36.         System.out.println("Test exception");  
  37.         Object obj = null;  
  38.         obj.toString();  
  39.     }  
  40.    
  41.         // 忽略此测试方法  
  42.     @Ignore  
  43.     @Test  
  44.     public void testMathMax() {  
  45.           Assert.fail("没有实现");  
  46.     }  
  47.         // 使用“假设”来忽略测试方法  
  48.     @Test  
  49.     public void testAssume(){  
  50.         System.out.println("Test assume");  
  51.                 // 当假设失败时,则会停止运行,但这并不会意味测试方法失败。  
  52.         Assume.assumeTrue(false);  
  53.         Assert.fail("没有实现");  
  54.     }  
  55.    
  56.     @After  
  57.     public void tearDown() throws Exception {  
  58.         System.out.println("Tear down");  
  59.     }  
  60.    
  61.     @AfterClass  
  62.     public static void tearDownAfterClass() {  
  63.         System.out.println("Tear down After class");  
  64.     }  
  65.    
  66. }  

 

 

如果细心的话,会发现Junit3的package是junit.framework,而Junit4是org.junit
执行此用例后,控制台会输出

写道

Set up before class
Set up
Test Math.pow
Tear down
Set up
Test Math.min
Tear down
Set up
Test exception
Tear down
Set up
Test assume
Tear down
Tear down After class

 

 

可以看到,执行次序是@BeforeClass -> @Before -> @Test -> @After -> @Before -> @Test -> @After -> @AfterClass@Ignore会被忽略。

private ScopedProperty property;@Beforepublic void setup() { property = new ScopedProperty("prop", "true");}@Afterpublic void teardown() { // Does same things like above. property.close();}@Test// use that prop..

一般来说,单元测试任务包括

运行测试方法

在Eclipse中,可以直接在类名或测试方法上右击,在弹出的右击菜单中选择Run As -> JUnit Test。
在Mvn中,可以直接通过mvn test命令运行测试用例。
也可以通过Java方式调用,创建一个TestCase实例,然后重载runTest()方法,在其方法内调用测试方法(可以多个)。

  1. TestCase test = new Junit3TestCase("mathPow") {  
  2.         // 重载  
  3.     protected void runTest() throws Throwable {  
  4.         testMathPow();  
  5.     };  
  6. };  
  7. test.run();  

 

 

更加便捷地,可以在创建TestCase实例时直接传入测试方法名称,JUnit会自动调用此测试方法,如

  1. TestCase test = new Junit3TestCase("testMathPow");  
  2. test.run();  

 

 

private String oldValue;@Beforepublic void setup() { // setProperty returns the old value of that property. oldValue = System.setProperty("prop", "true");} @Afterpublic void teardown() { System.setProperty("prop", oldValue);}@Test// use that prop..

单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复改进重构之后的正确性。

单元测试是编写测试代码,用来检测特定的、明确的、细颗粒的功能。单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的。

这会有点啰嗦..基于Java 7的try-with-resources 语句的帮助类实现了同样的功能:Test.java

单元测试是编写测试代码,用来检测特定的、明确的、细颗粒的功能。单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的。

Assert

Junit3和Junit4都提供了一个Assert类(虽然package不同,但是大致差不多)。Assert类中定义了很多静态方法来进行断言。列表如下:

  • assertTrue(String message, boolean condition) 要求condition == true
  • assertFalse(String message, boolean condition) 要求condition == false
  • fail(String message) 必然失败,同样要求代码不可达
  • assertEquals(String message, XXX expected,XXX actual) 要求expected.equals(actual)
  • assertArrayEquals(String message, XXX[] expecteds,XXX [] actuals) 要求expected.equalsArray(actual)
  • assertNotNull(String message, Object object) 要求object!=null
  • assertNull(String message, Object object) 要求object==null
  • assertSame(String message, Object expected, Object actual) 要求expected == actual
  • assertNotSame(String message, Object unexpected,Object actual) 要求expected != actual
  • assertThat(String reason, T actual, Matcher matcher) 要求matcher.matches(actual) == true
/** * A JUnit test rule, which changes a property value within a test * and restores the original one afterwards. * See {@link ScopedProperty}. */public class ScopedPropertyRule extends ExternalResource { private final String key; private final String value; private ScopedProperty scopedProperty; public ScopedPropertyRule(final String key, final String value) { this.key =key; this.value = value; } @Override protected void before() throws Throwable { scopedProperty = new ScopedProperty(key, value); } @Override protected void after() { scopedProperty.close(); }}
  1. 接口功能测试:用来保证接口功能的正确性。
  2. 局部数据结构测试(不常用):用来保证接口中的数据结构是正确的
    1. 比如变量有无初始值
    2. 变量是否溢出
  3. 边界条件测试
    1. 变量没有赋值(即为NULL)
    2. 变量是数值(或字符)
      1. 主要边界:最小值,最大值,无穷大(对于DOUBLE等)
      2. 溢出边界(期望异常或拒绝服务):最小值-1,最大值 1
      3. 临近边界:最小值 1,最大值-1
    3. 变量是字符串
      1. 引用“字符变量”的边界
      2. 空字符串
      3. 对字符串长度应用“数值变量”的边界
    4. 变量是集合
      1. 空集合
      2. 对集合的大小应用“数值变量”的边界
      3. 调整次序:升序、降序
    5. 变量有规律
      1. 比如对于Math.sqrt,给出n^2-1,和n^2 1的边界
  4. 所有独立执行通路测试:保证每一条代码,每个分支都经过测试
    1. 代码覆盖率
      1. 语句覆盖:保证每一个语句都执行到了
      2. 判定覆盖(分支覆盖):保证每一个分支都执行到
      3. 条件覆盖:保证每一个条件都覆盖到true和false(即if、while中的条件语句)
      4. 路径覆盖:保证每一个路径都覆盖到
    2. 相关软件
      1. Cobertura:语句覆盖
      2. Emma: Eclipse插件Eclemma
  5. 各条错误处理通路测试:保证每一个异常都经过测试

单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复改进重构之后的正确性。

private String oldValue;@Beforepublic void setup() { // setProperty returns the old value of that property. oldValue = System.setProperty("prop", "true");} @Afterpublic void teardown() { if( oldValue == null ) { System.clearProperty("prop"); } else { System.setProperty("prop", oldValue); }}@Test// Use that property..

代码覆盖率

比较流行的工具是Emma和Jacoco,Ecliplse插件有eclemma。eclemma2.0之前采用的是Emma,之后采用的是Jacoco。这里主要介绍一下Jacoco。Eclmama由于是Eclipse插件,所以非常易用,就不多做介绍了。

JUNIT

JUnit是Java单元测试框架,已经在Eclipse中默认安装。目前主流的有JUnit3和JUnit4。JUnit3中,测试用例需要继承TestCase类。JUnit4中,测试用例无需继承TestCase类,只需要使用@Test等注解。

当然,最好还是直接实现Junit规则,在这种情况下你不需要写@After。例如:Test.java

 

代码覆盖率

比较流行的工具是Emma和Jacoco,Ecliplse插件有eclemma。eclemma2.0之前采用的是Emma,之后采用的是Jacoco。这里主要介绍一下Jacoco。Eclmama由于是Eclipse插件,所以非常易用,就不多做介绍了。

属性在@BeforeClass中进行设置,但会在@AfterClass中恢复成原始的状态。结论:无副作用的测试可以提升自动化测试质量要避免由于执行顺序引起的测试错误,单元测试必须做到没有副作用。现实世界的例子中证明这个话题的关联性。更深入这个练习,我们开发了一些用于系统属性的帮助类。相同的原理同样适用于其他环境对象,例如线程属性,Java安全管理器和类似ScopedProperty和ScopedPropertyRule之类的类,这些都可以很容易的实现。当我们在不断努力提升自动测试测试质量时,我对其他最佳实践保持兴趣。给我们留言,说一下你在开发团队里面的工作。附录ScopedProperty.java

JUNIT

JUnit是Java单元测试框架,已经在Eclipse中默认安装。目前主流的有JUnit3和JUnit4。JUnit3中,测试用例需要继承TestCase类。JUnit4中,测试用例无需继承TestCase类,只需要使用@Test等注解。

Junit TestSuite

TestSuite是测试用例套件,能够运行过个测试方法。如果不指定TestSuite,会创建一个默认的TestSuite。默认TestSuite会扫描当前内中的所有测试方法,然后运行。
如果不想采用默认的TestSuite,则可以自定义TestSuite。在TestCase中,可以通过静态方法suite()返回自定义的suite。

  1. import junit.framework.Assert;  
  2. import junit.framework.Test;  
  3. import junit.framework.TestCase;  
  4. import junit.framework.TestSuite;  
  5.    
  6. public class Junit3TestCase extends TestCase {  
  7.         //...  
  8.     public static Test suite() {  
  9.         System.out.println("create suite");  
  10.         TestSuite suite = new TestSuite();  
  11.         suite.addTest(new Junit3TestCase("testMathPow"));  
  12.         return suite;  
  13.     }  
  14. }  

 

 

允许上述方法,控制台输出

写道

create suite
Set up
Test Math.pow
Tear down

 

 

并且只运行了testMathPow测试方法,而没有运行testMathMin测试方法。通过显式指定测试方法,可以控制测试执行的顺序。

也可以通过Java的方式创建TestSuite,然后调用TestCase,如

 

  1. // 先创建TestSuite,再添加测试方法  
  2. TestSuite testSuite = new TestSuite();  
  3. testSuite.addTest(new Junit3TestCase("testMathPow"));  
  4.    
  5. // 或者 传入Class,TestSuite会扫描其中的测试方法。  
  6. TestSuite testSuite = new TestSuite(Junit3TestCase.class,Junit3TestCase2.class,Junit3TestCase3.class);  
  7.    
  8. // 运行testSuite  
  9. TestResult testResult = new TestResult();  
  10. testSuite.run(testResult);  

 

 

 

testResult中保存了很多测试数据,包括运行测试方法数目(runCount)等。

这里的ScopedProperty实现了AutoCloseable来完成try-with-ressouces语句ScopedPropertyRule.java

Assert

Junit3和Junit4都提供了一个Assert类(虽然package不同,但是大致差不多)。Assert类中定义了很多静态方法来进行断言。列表如下:

  • assertTrue(String message, boolean condition) 要求condition == true
  • assertFalse(String message, boolean condition) 要求condition == false
  • fail(String message) 必然失败,同样要求代码不可达
  • assertEquals(String message, XXX expected,XXX actual) 要求expected.equals(actual)
  • assertArrayEquals(String message, XXX[] expecteds,XXX [] actuals) 要求expected.equalsArray(actual)
  • assertNotNull(String message, Object object) 要求object!=null
  • assertNull(String message, Object object) 要求object==null
  • assertSame(String message, Object expected, Object actual) 要求expected == actual
  • assertNotSame(String message, Object unexpected,Object actual) 要求expected != actual
  • assertThat(String reason, T actual, Matcher matcher) 要求matcher.matches(actual) == true

Mockito

Mockito是Google Code上的一个开源项目,Api相对于EasyMock更好友好。与EasyMock不同的是,Mockito没有录制过程,只需要在“运行测试代码”之前对接口进行Stub,也即设置方法的返回值或抛出的异常,然后直接运行测试代码,运行期间调用Mock的方法,会返回预先设置的返回值或抛出异常,最后再对测试代码进行验证。可以查看此文章了解两者的不同。
官方提供了很多样例,基本上包括了所有功能,可以去看看。
这里从官方样例中摘录几个典型的:

  • 验证调用行为

    1. import static org.mockito.Mockito.*;  
    2.    
    3. //创建Mock  
    4. List mockedList = mock(List.class);  
    5.    
    6. //使用Mock对象  
    7. mockedList.add("one");  
    8. mockedList.clear();  
    9.    
    10. //验证行为  
    11. verify(mockedList).add("one");  
    12. verify(mockedList).clear();     
  • 对Mock对象进行Stub

    1. //也可以Mock具体的类,而不仅仅是接口  
    2. LinkedList mockedList = mock(LinkedList.class);  
    3.    
    4. //Stub  
    5. when(mockedList.get(0)).thenReturn("first"); // 设置返回值  
    6. when(mockedList.get(1)).thenThrow(new RuntimeException()); // 抛出异常  
    7.    
    8. //第一个会打印 "first"  
    9. System.out.println(mockedList.get(0));  
    10.    
    11. //接下来会抛出runtime异常  
    12. System.out.println(mockedList.get(1));  
    13.    
    14. //接下来会打印"null",这是因为没有stub get(999)  
    15. System.out.println(mockedList.get(999));  
    16.     
    17. // 可以选择性地验证行为,比如只关心是否调用过get(0),而不关心是否调用过get(1)  
    18. verify(mockedList).get(0);     

单元测试的重要性不言而喻,但是对于经常会出现“失败测试”的现象我们该如何解决呢?失败的原因在哪?单元测试又有哪些副作用?作者Thomas Klambauer在此以最佳实践为例讲述如何提升自动测试的质量。原文内容编译如下:我们超过10K的单元测试大部分都是用JAVA的JUnit编写,并且用gradle自动构建工具运行。当我们添加越多的测试用例,就越经常遇到单元测试执行不稳定的问题。新添加的测试代码影响了现存的测试的执行。我们的”失败测试“(failed test)标准在它开始增加前一直表现地很不错。显然我们应该去抱怨那些糟糕的程序代码。但经过仔细的分析,我们发现造成不稳定的测试结果的真正原因。大多数由新测试造成的问题都是由于那些测试用例对测试环境作了一些不利的影响,因此也影响了其他测试的执行。在这篇文章中,我会展示我们是怎么找出这些特定的失败测试的根因,并且由此得出的对测试环境友好的单元测试设计的最佳实践。这一切都因为缺少单元测试时间上个星期,我们发现一组测试没有毫无预兆就失败了。我自愿参加分析。一组验证ThreadPool实现的重要且必须行为的单元测试。特别是,定时任务必须在特定异常抛出后继续运行。为了测试它,必须要有另外一个定时任务,抛出特定的异常。然后测试会在第一次因为异常失败后等待第二次执行。在某些机器上运行这些测试,它们会在重新运行时超时。尽管有各种各样的异常处理器可以记录异常,但没有任何输出。只有这样一条消息输出:

Java Report

可以使用Ant、Mvn或Eclipse来分析jacoco.exec文件,也可以通过API来分析。

Java代码  澳门新蒲京娱乐场官网 1

  1. public void createReport() throws Exception {  
  2.             // 读取监控结果  
  3.     final FileInputStream fis = new FileInputStream(new File("jacoco.exec"));  
  4.     final ExecutionDataReader executionDataReader = new ExecutionDataReader(fis);  
  5.             // 执行数据信息  
  6.     ExecutionDataStore executionDataStore = new ExecutionDataStore();  
  7.             // 会话信息  
  8.     SessionInfoStore sessionInfoStore = new SessionInfoStore();  
  9.    
  10.     executionDataReader.setExecutionDataVisitor(executionDataStore);  
  11.     executionDataReader.setSessionInfoVisitor(sessionInfoStore);  
  12.    
  13.     while (executionDataReader.read()) {  
  14.     }  
  15.    
  16.     fis.close();  
  17.                
  18.             // 分析结构  
  19.             final CoverageBuilder coverageBuilder = new CoverageBuilder();  
  20.     final Analyzer analyzer = new Analyzer(executionDataStore, coverageBuilder);  
  21.    
  22.             // 传入监控时的Class文件目录,注意必须与监控时的一样  
  23.     File classesDirectory = new File("classes");  
  24.     analyzer.analyzeAll(classesDirectory);  
  25.    
  26.     IBundleCoverage bundleCoverage = coverageBuilder.getBundle("Title");  
  27.             // 输出报告  
  28.         File reportDirectory = new File("report"); // 报告所在的目录  
  29.     final HTMLFormatter htmlFormatter = new HTMLFormatter();  // HTML格式  
  30.     final IReportVisitor visitor = htmlFormatter.createVisitor(new FileMultiReportOutput(reportDirectory));  
  31.             // 必须先调用visitInfo  
  32.     visitor.visitInfo(sessionInfoStore.getInfos(), executionDataStore.getContents());  
  33.     File sourceDirectory = new File("src"); // 源代码目录  
  34.             // 遍历所有的源代码  
  35.             // 如果不执行此过程,则在报告中只能看到方法名,但是无法查看具体的覆盖(因为没有源代码页面)  
  36.     visitor.visitBundle(bundleCoverage, new DirectorySourceFileLocator(sourceDirectory, "utf-8", 4));  
  37.             // 执行完毕  
  38.     visitor.visitEnd();  
  39. }   

Jacoco

Jacoco可以嵌入到Ant、Maven中,也可以使用Java Agent技术监控任意Java程序,也可以使用Java Api来定制功能。
Jacoco会监控JVM中的调用,生成监控结果(默认保存在jacoco.exec文件中),然后分析此结果,配合源代码生成覆盖率报告。需要注意的是:监控和分析这两步,必须使用相同的Class文件,否则由于Class不同,而无法定位到具体的方法,导致覆盖率均为0%。

然后,在Eclipse中运行时,这种情况永远都不可能重现,它只会出现在我们使用gradle来跑这些test的时候。下一步:我配置gradle打开调试端口,然后使用eclipse连接以确定原因。这才发现NullPointerException是在gradle的某处代码中抛出。我下载了源代码,发现System.getProperty(“line.separator”)返回null,并且被取消引用。有了这些信息,我检查代码,并且迅速发现另外一个校验不同平台上的字符串格式的测试在修改line.separtor属性时有不良影响。在测试完成后调用的System.clearProperty(“line.separator”),它无意中把该属性设置为null。在这种情况下,当下个测试运行时,由于它使用System.setOut重定向到控制台输出,日志信息会被写出到控制台,gradle代码会被调用。这种顺序的调用会导致gradle在获取line.separator属性值是抛出NullPointerException。注意,执行的顺序很重要,因此在ThreadPool测试时只是偶尔会出现。通常一系列的处理器都可以捕获到它,但由于它们也写出到控制台,当它们遇到NullPointerException时,会向上抛出。这个错误的快速的解决方式是去除clearProperty这个调用,而换用上一个line.separator属性的值。但我们是否可以做到更好呢?单元测试的副作用上面的部分错误是本来并且应该由测试自动化去处理的,JVM通常会重用同一个项目内的对象。因此理想情况下的单元测试应该对测试环境没有副作用,可以避免类似的错误。测试环境包括各种可以影响之后执行的测试的资源,如在文件系统中创建文件或者上面的情况,修改java系统属性。对于文件类的,Junit提供了TemporaryFolder规则用于创建临时文件,所以我们这里开发了一个属性的类似机制。解决方案:单元测试中恰当使用系统属性通常情况下,假设我们要开发一个测试用例,需要设置Java系统变量为一个特定值,可能会如下:Test.java

原文见此处

原文见此处

@Beforepublic void setup() { System.setProperty("prop", "true");}@Test// use that prop.

运行测试方法

与Junit3类似,可以在Eclipse中运行,也可以通过mvn test命令运行。

Exception: java.lang.NullPointerException thrown from the UncaughtExceptionHandler in thread “pool-unit-test-thread-1″

Junit3

先看一个Junit3的样例

Java代码  澳门新蒲京娱乐场官网 2

  1. // 测试java.lang.Math  
  2. // 必须继承TestCase  
  3. public class Junit3TestCase extends TestCase {  
  4.     public Junit3TestCase() {  
  5.         super();  
  6.     }  
  7.     
  8.         // 传入测试用例名称  
  9.     public Junit3TestCase(String name) {  
  10.         super(name);  
  11.     }  
  12.    
  13.         // 在每个Test运行之前运行  
  14.     @Override  
  15.     protected void setUp() throws Exception {  
  16.         System.out.println("Set up");  
  17.     }  
  18.         // 测试方法。  
  19.         // 方法名称必须以test开头,没有参数,无返回值,是公开的,可以抛出异常  
  20.         // 也即类似public void testXXX() throws Exception {}  
  21.     public void testMathPow() {  
  22.         System.out.println("Test Math.pow");  
  23.         Assert.assertEquals(4.0, Math.pow(2.0, 2.0));  
  24.     }  
  25.    
  26.     public void testMathMin() {  
  27.         System.out.println("Test Math.min");  
  28.         Assert.assertEquals(2.0, Math.min(2.0, 4.0));  
  29.     }  
  30.    
  31.         // 在每个Test运行之后运行  
  32.     @Override  
  33.     protected void tearDown() throws Exception {  
  34.         System.out.println("Tear down");  
  35.     }  
  36. }  

 

 

如果采用默认的TestSuite,则测试方法必须是public void testXXX() [throws Exception] {}的形式,并且不能存在依赖关系,因为测试方法的调用顺序是不可预知的。
上例执行后,控制台会输出

Text代码  澳门新蒲京娱乐场官网 3

  1. Set up  
  2. Test Math.pow  
  3. Tear down  
  4. Set up  
  5. Test Math.min  
  6. Tear down  

 

 

从中,可以猜测到,对于每个测试方法,调用的形式是:

Java代码  澳门新蒲京娱乐场官网 4

  1. testCase.setUp();  
  2. testCase.testXXX();  
  3. testCase.tearDown();     

 

 

Java Agent嵌入

首先,需要下载jacocoagent.jar文件,然后在Java程序启动参数后面加上 -javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2],具体的options可以在此页面找到。默认会在JVM关闭时(注意不能是kill -9),输出监控结果到jacoco.exec文件中,也可以通过socket来实时地输出监控报告(可以在Example代码中找到简单实现)。

@Testpublic void test() { try(ScopedProperty prop = new ScopedProperty("prop", "true")) { // use that prop.. }}
运行测试方法

在Eclipse中,可以直接在类名或测试方法上右击,在弹出的右击菜单中选择Run As -> JUnit Test。
在Mvn中,可以直接通过mvn test命令运行测试用例。
也可以通过Java方式调用,创建一个TestCase实例,然后重载runTest()方法,在其方法内调用测试方法(可以多个)。

Java代码  澳门新蒲京娱乐场官网 5

  1. TestCase test = new Junit3TestCase("mathPow") {  
  2.         // 重载  
  3.     protected void runTest() throws Throwable {  
  4.         testMathPow();  
  5.     };  
  6. };  
  7. test.run();  

 

 

更加便捷地,可以在创建TestCase实例时直接传入测试方法名称,JUnit会自动调用此测试方法,如

Java代码  澳门新蒲京娱乐场官网 6

  1. TestCase test = new Junit3TestCase("testMathPow");  
  2. test.run();  

 

 

一般来说,单元测试任务包括

这个类使用了ExternalResource来实现Junit测试规则。英文原文:compuware 翻译:ImportNew.com - 陈 晓舜

EasyMock

IBM上有几篇介绍EasyMock使用方法和原理的文章:EasyMock 使用方法与原理剖析,使用 EasyMock 更轻松地进行测试。
EasyMock把测试过程分为三步:录制、运行测试代码、验证期望。
录制过程大概就是:期望method(params)执行times次(默认一次),返回result(可选),抛出exception异常(可选)。
验证期望过程将会检查方法的调用次数。
一个简单的样例是:

 

Java代码  澳门新蒲京娱乐场官网 7

  1. @Test  
  2. public void testListInEasyMock() {  
  3.     List list = EasyMock.createMock(List.class);  
  4.     // 录制过程  
  5.    
  6.     // 期望方法list.set(0,1)执行2次,返回null,不抛出异常  
  7.     expect1: EasyMock.expect(list.set(0, 1)).andReturn(null).times(2);  
  8.     // 期望方法list.set(0,1)执行1次,返回null,不抛出异常  
  9.     expect2: EasyMock.expect(list.set(0, 1)).andReturn(1);  
  10.    
  11.     // 执行测试代码  
  12.     EasyMock.replay(list);  
  13.         // 执行list.set(0,1),匹配expect1期望,会返回null  
  14.     Assert.assertNull(list.set(0, 1));  
  15.         // 执行list.set(0,1),匹配expect1(因为expect1期望执行此方法2次),会返回null  
  16.     Assert.assertNull(list.set(0, 1));  
  17.         // 执行list.set(0,1),匹配expect2,会返回1  
  18.     Assert.assertEquals(1, list.set(0, 1));  
  19.    
  20.     // 验证期望  
  21.     EasyMock.verify(list);  
  22. }  

 

 

 

EasyMock还支持严格的检查,要求执行的方法次序与期望的完全一致。

Junit3

先看一个Junit3的样例

  1. // 测试java.lang.Math  
  2. // 必须继承TestCase  
  3. public class Junit3TestCase extends TestCase {  
  4.     public Junit3TestCase() {  
  5.         super();  
  6.     }  
  7.     
  8.         // 传入测试用例名称  
  9.     public Junit3TestCase(String name) {  
  10.         super(name);  
  11.     }  
  12.    
  13.         // 在每个Test运行之前运行  
  14.     @Override  
  15.     protected void setUp() throws Exception {  
  16.         System.out.println("Set up");  
  17.     }  
  18.         // 测试方法。  
  19.         // 方法名称必须以test开头,没有参数,无返回值,是公开的,可以抛出异常  
  20.         // 也即类似public void testXXX() throws Exception {}  
  21.     public void testMathPow() {  
  22.         System.out.println("Test Math.pow");  
  23.         Assert.assertEquals(4.0, Math.pow(2.0, 2.0));  
  24.     }  
  25.    
  26.     public void testMathMin() {  
  27.         System.out.println("Test Math.min");  
  28.         Assert.assertEquals(2.0, Math.min(2.0, 4.0));  
  29.     }  
  30.    
  31.         // 在每个Test运行之后运行  
  32.     @Override  
  33.     protected void tearDown() throws Exception {  
  34.         System.out.println("Tear down");  
  35.     }  
  36. }  

 

 

如果采用默认的TestSuite,则测试方法必须是public void testXXX() [throws Exception] {}的形式,并且不能存在依赖关系,因为测试方法的调用顺序是不可预知的。
上例执行后,控制台会输出

  1. Set up  
  2. Test Math.pow  
  3. Tear down  
  4. Set up  
  5. Test Math.min  
  6. Tear down  

 

 

从中,可以猜测到,对于每个测试方法,调用的形式是:

  1. testCase.setUp();  
  2. testCase.testXXX();  
  3. testCase.tearDown();     

 

 

但当其他在其之后运行的测试都运行在同一个JVM下,这个属性值还是会保留被设的值,并且会影响在后面运行的测试用例行为。所以,让我们来改进一下:Test.java

Mockito

Mockito是Google Code上的一个开源项目,Api相对于EasyMock更好友好。与EasyMock不同的是,Mockito没有录制过程,只需要在“运行测试代码”之前对接口进行Stub,也即设置方法的返回值或抛出的异常,然后直接运行测试代码,运行期间调用Mock的方法,会返回预先设置的返回值或抛出异常,最后再对测试代码进行验证。可以查看此文章了解两者的不同。
官方提供了很多样例,基本上包括了所有功能,可以去看看。
这里从官方样例中摘录几个典型的:

  • 验证调用行为 Java代码  澳门新蒲京娱乐场官网 8

    1. import static org.mockito.Mockito.*;  
    2.    
    3. //创建Mock  
    4. List mockedList = mock(List.class);  
    5.    
    6. //使用Mock对象  
    7. mockedList.add("one");  
    8. mockedList.clear();  
    9.    
    10. //验证行为  
    11. verify(mockedList).add("one");  
    12. verify(mockedList).clear();     
  • 对Mock对象进行Stub Java代码  澳门新蒲京娱乐场官网 9

    1. //也可以Mock具体的类,而不仅仅是接口  
    2. LinkedList mockedList = mock(LinkedList.class);  
    3.    
    4. //Stub  
    5. when(mockedList.get(0)).thenReturn("first"); // 设置返回值  
    6. when(mockedList.get(1)).thenThrow(new RuntimeException()); // 抛出异常  
    7.    
    8. //第一个会打印 "first"  
    9. System.out.println(mockedList.get(0));  
    10.    
    11. //接下来会抛出runtime异常  
    12. System.out.println(mockedList.get(1));  
    13.    
    14. //接下来会打印"null",这是因为没有stub get(999)  
    15. System.out.println(mockedList.get(999));  
    16.     
    17. // 可以选择性地验证行为,比如只关心是否调用过get(0),而不关心是否调用过get(1)  
    18. verify(mockedList).get(0);     

Mock/Stub

Mock和Stub是两种测试代码功能的方法。Mock测重于对功能的模拟。Stub测重于对功能的测试重现。比如对于List接口,Mock会直接对List进行模拟,而Stub会新建一个实现了List的TestList,在其中编写测试的代码。
强烈建议优先选择Mock方式,因为Mock方式下,模拟代码与测试代码放在一起,易读性好,而且扩展性、灵活性都比Stub好。
比较流行的Mock有:

  • JMock
  • EasyMock
  • Mockito
  • powermock

其中EasyMock和Mockito对于Java接口使用接口代理的方式来模拟,对于Java类使用继承的方式来模拟(也即会创建一个新的Class类)。Mockito支持spy方式,可以对实例进行模拟。但它们都不能对静态方法和final类进行模拟,powermock通过修改字节码来支持了此功能。

/** * A helper to switch a system property value and restore the previous one. * * When used in try-with-resources, restores the values automatically. */public class ScopedProperty implements AutoCloseable { private final String key; private final String oldValue; /** * * @param key The System.setProperty key * @param value The System.setProperty value to switch to. */ public ScopedProperty(final String key, final String value) { this.key = key; oldValue = System.setProperty(key, value); } @Override public void close() { // Can't use setProperty(key, null) - Throws NullPointerException. if( oldValue == null ) { // Previously there was no entry. System.clearProperty(key); } else { System.setProperty(key, oldValue); } }}

JUnit4

与JUnit3不同,JUnit4通过注解的方式来识别测试方法。目前支持的主要注解有:

  • @BeforeClass 全局只会执行一次,而且是第一个运行
  • @Before 在测试方法运行之前运行
  • @Test 测试方法
  • @After 在测试方法运行之后允许
  • @AfterClass 全局只会执行一次,而且是最后一个运行
  • @Ignore 忽略此方法

下面举一个样例:

Java代码  澳门新蒲京娱乐场官网 10

  1. import org.junit.After;  
  2. import org.junit.AfterClass;  
  3. import org.junit.Assert;  
  4. import org.junit.Before;  
  5. import org.junit.BeforeClass;  
  6. import org.junit.Ignore;  
  7. import org.junit.Test;  
  8.    
  9. public class Junit4TestCase {  
  10.    
  11.     @BeforeClass  
  12.     public static void setUpBeforeClass() {  
  13.         System.out.println("Set up before class");  
  14.     }  
  15.    
  16.     @Before  
  17.     public void setUp() throws Exception {  
  18.         System.out.println("Set up");  
  19.     }  
  20.    
  21.     @Test  
  22.     public void testMathPow() {  
  23.         System.out.println("Test Math.pow");  
  24.         Assert.assertEquals(4.0, Math.pow(2.0, 2.0), 0.0);  
  25.     }  
  26.    
  27.     @Test  
  28.     public void testMathMin() {  
  29.         System.out.println("Test Math.min");  
  30.         Assert.assertEquals(2.0, Math.min(2.0, 4.0), 0.0);  
  31.     }  
  32.    
  33.         // 期望此方法抛出NullPointerException异常  
  34.     @Test(expected = NullPointerException.class)  
  35.     public void testException() {  
  36.         System.out.println("Test exception");  
  37.         Object obj = null;  
  38.         obj.toString();  
  39.     }  
  40.    
  41.         // 忽略此测试方法  
  42.     @Ignore  
  43.     @Test  
  44.     public void testMathMax() {  
  45.           Assert.fail("没有实现");  
  46.     }  
  47.         // 使用“假设”来忽略测试方法  
  48.     @Test  
  49.     public void testAssume(){  
  50.         System.out.println("Test assume");  
  51.                 // 当假设失败时,则会停止运行,但这并不会意味测试方法失败。  
  52.         Assume.assumeTrue(false);  
  53.         Assert.fail("没有实现");  
  54.     }  
  55.    
  56.     @After  
  57.     public void tearDown() throws Exception {  
  58.         System.out.println("Tear down");  
  59.     }  
  60.    
  61.     @AfterClass  
  62.     public static void tearDownAfterClass() {  
  63.         System.out.println("Tear down After class");  
  64.     }  
  65.    
  66. }  

 

 

如果细心的话,会发现Junit3的package是junit.framework,而Junit4是org.junit
执行此用例后,控制台会输出

写道

Set up before class
Set up
Test Math.pow
Tear down
Set up
Test Math.min
Tear down
Set up
Test exception
Tear down
Set up
Test assume
Tear down
Tear down After class

 

 

可以看到,执行次序是@BeforeClass -> @Before -> @Test -> @After -> @Before -> @Test -> @After -> @AfterClass@Ignore会被忽略。

Java Report

可以使用Ant、Mvn或Eclipse来分析jacoco.exec文件,也可以通过API来分析。

  1. public void createReport() throws Exception {  
  2.             // 读取监控结果  
  3.     final FileInputStream fis = new FileInputStream(new File("jacoco.exec"));  
  4.     final ExecutionDataReader executionDataReader = new ExecutionDataReader(fis);  
  5.             // 执行数据信息  
  6.     ExecutionDataStore executionDataStore = new ExecutionDataStore();  
  7.             // 会话信息  
  8.     SessionInfoStore sessionInfoStore = new SessionInfoStore();  
  9.    
  10.     executionDataReader.setExecutionDataVisitor(executionDataStore);  
  11.     executionDataReader.setSessionInfoVisitor(sessionInfoStore);  
  12.    
  13.     while (executionDataReader.read()) {  
  14.     }  
  15.    
  16.     fis.close();  
  17.                
  18.             // 分析结构  
  19.             final CoverageBuilder coverageBuilder = new CoverageBuilder();  
  20.     final Analyzer analyzer = new Analyzer(executionDataStore, coverageBuilder);  
  21.    
  22.             // 传入监控时的Class文件目录,注意必须与监控时的一样  
  23.     File classesDirectory = new File("classes");  
  24.     analyzer.analyzeAll(classesDirectory);  
  25.    
  26.     IBundleCoverage bundleCoverage = coverageBuilder.getBundle("Title");  
  27.             // 输出报告  
  28.         File reportDirectory = new File("report"); // 报告所在的目录  
  29.     final HTMLFormatter htmlFormatter = new HTMLFormatter();  // HTML格式  
  30.     final IReportVisitor visitor = htmlFormatter.createVisitor(new FileMultiReportOutput(reportDirectory));  
  31.             // 必须先调用visitInfo  
  32.     visitor.visitInfo(sessionInfoStore.getInfos(), executionDataStore.getContents());  
  33.     File sourceDirectory = new File("src"); // 源代码目录  
  34.             // 遍历所有的源代码  
  35.             // 如果不执行此过程,则在报告中只能看到方法名,但是无法查看具体的覆盖(因为没有源代码页面)  
  36.     visitor.visitBundle(bundleCoverage, new DirectorySourceFileLocator(sourceDirectory, "utf-8", 4));  
  37.             // 执行完毕  
  38.     visitor.visitEnd();  
  39. }  

or also:或者:Test.java

Java Agent嵌入

首先,需要下载jacocoagent.jar文件,然后在Java程序启动参数后面加上 -javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2],具体的options可以在此页面找到。默认会在JVM关闭时(注意不能是kill -9),输出监控结果到jacoco.exec文件中,也可以通过socket来实时地输出监控报告(可以在Example代码中找到简单实现)。

EasyMock

IBM上有几篇介绍EasyMock使用方法和原理的文章:EasyMock 使用方法与原理剖析,使用 EasyMock 更轻松地进行测试。
EasyMock把测试过程分为三步:录制、运行测试代码、验证期望。
录制过程大概就是:期望method(params)执行times次(默认一次),返回result(可选),抛出exception异常(可选)。
验证期望过程将会检查方法的调用次数。
一个简单的样例是:

 

  1. @Test  
  2. public void testListInEasyMock() {  
  3.     List list = EasyMock.createMock(List.class);  
  4.     // 录制过程  
  5.    
  6.     // 期望方法list.set(0,1)执行2次,返回null,不抛出异常  
  7.     expect1: EasyMock.expect(list.set(0, 1)).andReturn(null).times(2);  
  8.     // 期望方法list.set(0,1)执行1次,返回null,不抛出异常  
  9.     expect2: EasyMock.expect(list.set(0, 1)).andReturn(1);  
  10.    
  11.     // 执行测试代码  
  12.     EasyMock.replay(list);  
  13.         // 执行list.set(0,1),匹配expect1期望,会返回null  
  14.     Assert.assertNull(list.set(0, 1));  
  15.         // 执行list.set(0,1),匹配expect1(因为expect1期望执行此方法2次),会返回null  
  16.     Assert.assertNull(list.set(0, 1));  
  17.         // 执行list.set(0,1),匹配expect2,会返回1  
  18.     Assert.assertEquals(1, list.set(0, 1));  
  19.    
  20.     // 验证期望  
  21.     EasyMock.verify(list);  
  22. }  

 

 

 

EasyMock还支持严格的检查,要求执行的方法次序与期望的完全一致。

@ClassRulepublic static ScopedPropertyRule prop = new ScopedPropertyRule("prop", "true");@Testpublic void test1() { // use that prop..}@Testpublic void test2() {// use that prop..}
运行测试方法

与Junit3类似,可以在Eclipse中运行,也可以通过mvn test命令运行。

  1. 接口功能测试:用来保证接口功能的正确性。
  2. 局部数据结构测试(不常用):用来保证接口中的数据结构是正确的
    1. 比如变量有无初始值
    2. 变量是否溢出
  3. 边界条件测试
    1. 变量没有赋值(即为NULL)
    2. 变量是数值(或字符)
      1. 主要边界:最小值,最大值,无穷大(对于DOUBLE等)
      2. 溢出边界(期望异常或拒绝服务):最小值-1,最大值 1
      3. 临近边界:最小值 1,最大值-1
    3. 变量是字符串
      1. 引用“字符变量”的边界
      2. 空字符串
      3. 对字符串长度应用“数值变量”的边界
    4. 变量是集合
      1. 空集合
      2. 对集合的大小应用“数值变量”的边界
      3. 调整次序:升序、降序
    5. 变量有规律
      1. 比如对于Math.sqrt,给出n^2-1,和n^2 1的边界
  4. 所有独立执行通路测试:保证每一条代码,每个分支都经过测试
    1. 代码覆盖率
      1. 语句覆盖:保证每一个语句都执行到了
      2. 判定覆盖(分支覆盖):保证每一个分支都执行到
      3. 条件覆盖:保证每一个条件都覆盖到true和false(即if、while中的条件语句)
      4. 路径覆盖:保证每一个路径都覆盖到
    2. 相关软件
      1. Cobertura:语句覆盖
      2. Emma: Eclipse插件Eclemma
  5. 各条错误处理通路测试:保证每一个异常都经过测试
Junit TestSuite

TestSuite是测试用例套件,能够运行过个测试方法。如果不指定TestSuite,会创建一个默认的TestSuite。默认TestSuite会扫描当前内中的所有测试方法,然后运行。
如果不想采用默认的TestSuite,则可以自定义TestSuite。在TestCase中,可以通过静态方法suite()返回自定义的suite。

Java代码  澳门新蒲京娱乐场官网 11

  1. import junit.framework.Assert;  
  2. import junit.framework.Test;  
  3. import junit.framework.TestCase;  
  4. import junit.framework.TestSuite;  
  5.    
  6. public class Junit3TestCase extends TestCase {  
  7.         //...  
  8.     public static Test suite() {  
  9.         System.out.println("create suite");  
  10.         TestSuite suite = new TestSuite();  
  11.         suite.addTest(new Junit3TestCase("testMathPow"));  
  12.         return suite;  
  13.     }  
  14. }  

 

 

允许上述方法,控制台输出

写道

create suite
Set up
Test Math.pow
Tear down

 

 

并且只运行了testMathPow测试方法,而没有运行testMathMin测试方法。通过显式指定测试方法,可以控制测试执行的顺序。

也可以通过Java的方式创建TestSuite,然后调用TestCase,如

 

Java代码  澳门新蒲京娱乐场官网 12

  1. // 先创建TestSuite,再添加测试方法  
  2. TestSuite testSuite = new TestSuite();  
  3. testSuite.addTest(new Junit3TestCase("testMathPow"));  
  4.    
  5. // 或者 传入Class,TestSuite会扫描其中的测试方法。  
  6. TestSuite testSuite = new TestSuite(Junit3TestCase.class,Junit3TestCase2.class,Junit3TestCase3.class);  
  7.    
  8. // 运行testSuite  
  9. TestResult testResult = new TestResult();  
  10. testSuite.run(testResult);  

 

 

 

testResult中保存了很多测试数据,包括运行测试方法数目(runCount)等。

Mock/Stub

Mock和Stub是两种测试代码功能的方法。Mock测重于对功能的模拟。Stub测重于对功能的测试重现。比如对于List接口,Mock会直接对List进行模拟,而Stub会新建一个实现了List的TestList,在其中编写测试的代码。
强烈建议优先选择Mock方式,因为Mock方式下,模拟代码与测试代码放在一起,易读性好,而且扩展性、灵活性都比Stub好。
比较流行的Mock有:

  • JMock
  • EasyMock
  • Mockito
  • powermock

其中EasyMock和Mockito对于Java接口使用接口代理的方式来模拟,对于Java类使用继承的方式来模拟(也即会创建一个新的Class类)。Mockito支持spy方式,可以对实例进行模拟。但它们都不能对静态方法和final类进行模拟,powermock通过修改字节码来支持了此功能。

Jacoco

Jacoco可以嵌入到Ant、Maven中,也可以使用Java Agent技术监控任意Java程序,也可以使用Java Api来定制功能。
Jacoco会监控JVM中的调用,生成监控结果(默认保存在jacoco.exec文件中),然后分析此结果,配合源代码生成覆盖率报告。需要注意的是:监控和分析这两步,必须使用相同的Class文件,否则由于Class不同,而无法定位到具体的方法,导致覆盖率均为0%。

本文由www.5197.com发布于web前端,转载请注明出处:【java测试】Junit、Mock 代码覆盖率澳门新蒲京娱乐