Junit
使用方法
在需要测试的方法上加@Test注解即可
import static org.junit.jupiter.api.Assertions.assertEquals;import org.junit.jupiter.api.Test;class FirstJUnit5Tests { @Test void myFirstTest() { assertEquals(2, 1 + 1); }}注解
| 注解 | 描述 |
|---|---|
| @Test | 表示方法是测试方法。与JUnit4的@Test注解不同的是,这个注解没有声明任何属性,因为JUnit Jupiter中的测试扩展是基于他们自己的专用注解来操作的。除非被覆盖,否则这些方法可以继承。 |
| @ParameterizedTest | 表示方法是参数化测试。 除非被覆盖,否则这些方法可以继承。 |
| @RepeatedTest | 表示方法是用于重复测试的测试模板。除非被覆盖,否则这些方法可以继承。 |
| @TestFactory | 表示方法是用于动态测试的测试工厂。除非被覆盖,否则这些方法可以继承。 |
| @TestInstance | 用于为被注解的测试类配置测试实例生命周期。 这个注解可以继承。 |
| @TestTemplate | 表示方法是测试用例的模板,设计为被调用多次,调用次数取决于自注册的提供者返回的调用上下文。除非被覆盖,否则这些方法可以继承。 |
| @DisplayName | 声明测试类或测试方法的自定义显示名称。这个注解不被继承。 |
| @BeforeEach | 表示被注解的方法应在当前类的每个@Test,@RepeatedTest,@ParameterizedTest或@TestFactory方法之前执行; 类似于JUnit 4的@Before。 除非被覆盖,否则这些方法可以继承。 |
| @AfterEach | 表示被注解的方法应在当前类的每个@Test,@RepeatedTest,@ParameterizedTest或@TestFactory方法之后执行; 类似于JUnit 4的@After。 除非被覆盖,否则这些方法可以继承。 |
| @BeforeAll | 表示被注解的方法应该在当前类的所有@Test,@RepeatedTest,@ParameterizedTest和@TestFactory方法之前执行; 类似于JUnit 4的@BeforeClass。 这样的方法可以继承(除非被隐藏或覆盖),并且必须是静态的(除非使用“per-class”测试实例生命周期)。 |
| @AfterAll | 表示被注解的方法应该在当前类的所有@Test,@RepeatedTest,@ParameterizedTest和@TestFactory方法之后执行; 类似于JUnit 4的@AfterClass。 这样的方法可以继承(除非被隐藏或覆盖),并且必须是静态的(除非使用“per-class”测试实例生命周期)。 |
| @Nested | 表示被注解的类是一个嵌套的非静态测试类。除非使用“per-class”测试实例生命周期,否则@BeforeAll和@AfterAll方法不能直接在@Nested测试类中使用。 这个注解不能继承。 |
| @Tag | 在类或方法级别声明标签,用于过滤测试; 类似于TestNG中的test group或JUnit 4中的Categories。这个注释可以在类级别上继承,但不能在方法级别上继承。 |
| @Disabled | 用于禁用测试类或测试方法; 类似于JUnit4的@Ignore。这个注解不能继承。 |
| @ExtendWith | 用于注册自定义扩展。 这个注解可以继承。 |
eg:
import static org.junit.jupiter.api.Assertions.fail;import org.junit.jupiter.api.AfterAll;import org.junit.jupiter.api.AfterEach;import org.junit.jupiter.api.BeforeAll;import org.junit.jupiter.api.BeforeEach;import org.junit.jupiter.api.Disabled;import org.junit.jupiter.api.Test;class StandardTests { @BeforeAll static void initAll() { } @BeforeEach void init() { } @Test void succeedingTest() { } @Test void failingTest() { fail("a failing test"); } @Test @Disabled("for demonstration purposes") void skippedTest() { // not executed } @AfterEach void tearDown() { } @AfterAll static void tearDownAll() { }}断言
package com.yiibai.junit;import static org.junit.Assert.*;import org.junit.Test;public class AssertionsTest { @Test public void test() { String obj1 = "junit"; String obj2 = "junit"; String obj3 = "test"; String obj4 = "test"; String obj5 = null; int var1 = 1; int var2 = 2; int[] arithmetic1 = { 1, 2, 3 }; int[] arithmetic2 = { 1, 2, 3 }; assertEquals(obj1, obj2); assertSame(obj3, obj4); assertNotSame(obj2, obj4); assertNotNull(obj1); assertNull(obj5); assertTrue(var1 var2); assertArrayEquals(arithmetic1, arithmetic2); }}//原文出自【易百教程】,商业转载请联系作者获得授权,非商业请保留原文链接:https://www.yiibai.com/junit/junit-assertions.htmlJmockit
基本使用
JMockit是一款Java类/接口/对象的Mock工具.
Java Mock工具很多,比如easyMock,Mockito等等。为什么要选择JMockit呢?其实也没有啥特别原因,Mock工具的原理都差不多,就看Mock工具封装的API是否易用了。JMockit的API易用,丰富! 写出来的Mock程序代码完全面向对象.
//一个简单的类,能用不同语言打招呼
public class HelloJMockit { // 向JMockit打招呼 public String sayHello() { Locale locale = Locale.getDefault(); if (locale.equals(Locale.CHINA)) { // 在中国,就说中文 return "你好,JMockit!"; } else { // 在其它国家,就说英文 return "Hello,JMockit!"; } }}//HelloJMockit类的测试类
public class HelloJMockitTest { /** * 测试场景:当前是在中国 */ @Test public void testSayHelloAtChina() { // 假设当前位置是在中国 new Expectations(Locale.class) { { Locale.getDefault(); result = Locale.CHINA; } }; // 断言说中文 Assert.assertTrue("你好,JMockit!".equals((new HelloJMockit()).sayHello())); } /** * 测试场景:当前是在美国 */ @Test public void testSayHelloAtUS() { // 假设当前位置是在美国 new Expectations(Locale.class) { { Locale.getDefault(); result = Locale.US; } }; // 断言说英文 Assert.assertTrue("Hello,JMockit!".equals((new HelloJMockit()).sayHello())); }}在上面的例子中,为了对依赖(当前的位置)进行Mock,用简单的3行代码即可搞定。把测试代码的依赖抽象成期待(Expectations),并把期待类Expectations作为本测试程序的内部类,可以任意访问本测试程序类的所有成员,为编写Mock程序提供极大便利。API面向对象特性封装良好。
此外,JMockit还提供了注解,支持泛型的Mock API用于对类/对象的属性,方法(支持static,private,final,native),构造函数,初始代码块(含静态初始代码块)灵活Mock。
jmockit常用api
@Mocked
当@Mocked修饰一个类时
//@Mocked注解用途public class MockedClassTest { // 加上了JMockit的API @Mocked, JMockit会帮我们实例化这个对象,不用担心它为null @Mocked Locale locale; // 当@Mocked作用于class @Test public void testMockedClass() { // 静态方法不起作用了,返回了null Assert.assertTrue(Locale.getDefault() == null); // 非静态方法(返回类型为String)也不起作用了,返回了null Assert.assertTrue(locale.getCountry() == null); // 自已new一个,也同样如此,方法都被mock了 Locale chinaLocale = new Locale("zh", "CN"); Assert.assertTrue(chinaLocale.getCountry() == null); }}当@Mocked修饰一个接口/抽象类时
//@Mocked注解用途public class MockedInterfaceTest { // 加上了JMockit的API @Mocked, JMockit会帮我们实例化这个对象,尽管这个对象的类型是一个接口,不用担心它为null @Mocked HttpSession session; // 当@Mocked作用于interface @Test public void testMockedInterface() { // (返回类型为String)也不起作用了,返回了null Assert.assertTrue(session.getId() == null); // (返回类型为原始类型)也不起作用了,返回了0 Assert.assertTrue(session.getCreationTime() == 0L); // (返回类型为原非始类型,非String,返回的对象不为空,这个对象也是JMockit帮你实例化的,同样这个实例化的对象也是一个Mocked对象) Assert.assertTrue(session.getServletContext() != null); // Mocked对象返回的Mocked对象,(返回类型为String)的方法也不起作用了,返回了null Assert.assertTrue(session.getServletContext().getContextPath() == null); }}@Mocked功能总结
通过上述例子,可以看出:@Mocked修饰的类/接口,是告诉JMockit,帮我生成一个Mocked对象,这个对象方法(包含静态方法)返回默认值。
即如果返回类型为原始类型(short,int,float,double,long)就返回0,如果返回类型为String就返回null,如果返回类型是其它引用类型,则返回这个引用类型的Mocked对象。
Expectations
Expectations主要有两种使用方式。
通过引用外部类的Mock对象(@Injectabe,@Mocked,@Capturing)来录制
//Expectations对外部类的mock对象进行录制public class ExpectationsTest { @Mocked Calendar cal; @Test public void testRecordOutside() { new Expectations() { { // 对cal.get方法进行录制,并匹配参数 Calendar.YEAR cal.get(Calendar.YEAR); result = 2016;// 年份不再返回当前小时。而是返回2016年 // 对cal.get方法进行录制,并匹配参数 Calendar.HOUR_OF_DAY cal.get(Calendar.HOUR_OF_DAY); result = 7;// 小时不再返回当前小时。而是返回早上7点钟 } }; Assert.assertTrue(cal.get(Calendar.YEAR) == 2016); Assert.assertTrue(cal.get(Calendar.HOUR_OF_DAY) == 7); // 因为没有录制过,所以这里月份返回默认值 0 Assert.assertTrue(cal.get(Calendar.DAY_OF_MONTH) == 0); }}在这个例子中,在Expectations匿名内部类的初始代码块中,我们可以对外部类的任意成员变量,方法进行调用。大大便利我们书写录制脚本。
通过构建函数注入类/对象来录制
在上面的例子中,我们通过引用外部类的Mock对象(@Injectabe,@Mocked,@Capturing)来录制,可是无论是@Injectabe,@Mocked,@Capturing哪种Mock对象,都是对类的方法都mock了,可是有时候,我们只希望JMockit只mock类/对象的某一个方法。怎么办? 看下面的例子就明白啦。
//通过Expectations对其构造函数mock对象进行录制public class ExpectationsConstructorTest2 { // 把类传入Expectations的构造函数 @Test public void testRecordConstrutctor1() { Calendar cal = Calendar.getInstance(); // 把待Mock的类传入Expectations的构造函数,可以达到只mock类的部分行为的目的 new Expectations(Calendar.class) { { // 只对get方法并且参数为Calendar.HOUR_OF_DAY进行录制 cal.get(Calendar.HOUR_OF_DAY); result = 7;// 小时永远返回早上7点钟 } }; Calendar now = Calendar.getInstance(); // 因为下面的调用mock过了,小时永远返回7点钟了 Assert.assertTrue(now.get(Calendar.HOUR_OF_DAY) == 7); // 因为下面的调用没有mock过,所以方法的行为不受mock影响, Assert.assertTrue(now.get(Calendar.DAY_OF_MONTH) == (new Date()).getDate()); } // 把对象传入Expectations的构造函数 @Test public void testRecordConstrutctor2() { Calendar cal = Calendar.getInstance(); // 把待Mock的对象传入Expectations的构造函数,可以达到只mock类的部分行为的目的,但只对这个对象影响 new Expectations(cal) { { // 只对get方法并且参数为Calendar.HOUR_OF_DAY进行录制 cal.get(Calendar.HOUR_OF_DAY); result = 7;// 小时永远返回早上7点钟 } }; // 因为下面的调用mock过了,小时永远返回7点钟了 Assert.assertTrue(cal.get(Calendar.HOUR_OF_DAY) == 7); // 因为下面的调用没有mock过,所以方法的行为不受mock影响, Assert.assertTrue(cal.get(Calendar.DAY_OF_MONTH) == (new Date()).getDate()); // now是另一个对象,上面录制只对cal对象的影响,所以now的方法行为没有任何变化 Calendar now = Calendar.getInstance(); // 不受mock影响 Assert.assertTrue(now.get(Calendar.HOUR_OF_DAY) == (new Date()).getHours()); // 不受mock影响 Assert.assertTrue(now.get(Calendar.DAY_OF_MONTH) == (new Date()).getDate()); }}MockUp & Mock
MockUp & @Mock提供的Mock方式
MockUp是使用最多的方法,该方式简单直接,可以清楚看到具体mock的类和方法
对于private和static方法,也可以使用MockUp,只需要去掉static关键字或者将private修改为public,再加上@Mock注解即可。
//Mockup & @Mock的Mock方式public class MockUpTest { @Test public void testMockUp() { // 对Java自带类Calendar的get方法进行定制 // 只需要把Calendar类传入MockUp类的构造函数即可 new MockUp<Calendar>(Calendar.class) { // 想Mock哪个方法,就给哪个方法加上@Mock, 没有@Mock的方法,不受影响 @Mock public int get(int unit) { if (unit == Calendar.YEAR) { return 2017; } if (unit == Calendar.MONDAY) { return 12; } if (unit == Calendar.DAY_OF_MONTH) { return 25; } if (unit == Calendar.HOUR_OF_DAY) { return 7; } return 0; } }; // 从此Calendar的get方法,就沿用你定制过的逻辑,而不是它原先的逻辑。 Calendar cal = Calendar.getInstance(Locale.FRANCE); Assert.assertTrue(cal.get(Calendar.YEAR) == 2017); Assert.assertTrue(cal.get(Calendar.MONDAY) == 12); Assert.assertTrue(cal.get(Calendar.DAY_OF_MONTH) == 25); Assert.assertTrue(cal.get(Calendar.HOUR_OF_DAY) == 7); // Calendar的其它方法,不受影响 Assert.assertTrue((cal.getFirstDayOfWeek() == Calendar.MONDAY)); }}