Junit系列一Junit Test Fixture(1)

测试环境设置过程的自动化,是测试中最具挑战性的部分,在单元测试、集成测试、系统测试中都是如此。测试执行所需要的固定环境称为Test Fixture。

Test Fixture(测试固件?)是指一个测试运行所需的固定环境,准确的定义:

The test fixture is everything we need to have in place to exercise the SUT

通俗点讲就是测试上下文或者说是测试环境,也是就是测试运行之前所需的稳定的、公共的可重复的运行环境,这个“环境”不仅可以是数据,也可以指对被测软件的准备,例如实例化被测方法所依赖的类、加载数据库等等。

不同级别的测试所需的Test Fixture是不同的:

Test Fixture Test Fixture例子
单元测试 •创建新对象•准备输入数据
集成测试 •将数据库重置为初始状态•复制在测试过程中使用的文件
系统测试 •安装提供运行时环境的虚拟•安装(或清理某些初始状态)的Web服务器和数据库由应用程序使用应用

因此,Fixtures 是测试中非常重要的一部分。他们的主要目的是建立一个固定/已知的环境状态以确保 测试可重复并且按照预期方式运行。Junit提供了一些方法来设置fixture,可以用来设置测试方法所需的环境数据等,允许你精确的定义你的Fixtures。大致上分为三类:

  • Test Fixtures
  • 规则(Rules&RulesClass)
  • Theories

Test Fixtures

JUnit提供可以在每个测试运行前后都运行fixture,或者在所有测试方法前后只运行一次fixture的注解。这允许我们在测试前后设置和清理数据或环境等测试条件。JUnit有两个类级别(@BeforeClass和@AfterClass),两个方法级别(@Afer和@Before)总共四个fixture的注解。

  • @Before - 在每个@Test方法之前运行
  • @After - 在每个@Test方法之后运行
  • @BeforeClass - 在所有的@Test方法之前运行一次
  • @AfterClass - 在所有的@Test方之后运行一次
Before&After

在编写测试时,通常会发现几个测试方法需要在运行之前需要类似的或者同样的对象。这个时候就可以使用@Before。@Before用来标注一个public void方法,该方法会再在@Test方法之前运行。超类的@Before方法将在当前类之前运行。

public class BeforeTest {

    /**
     * 该方法在test方法执行之前执行
     */
    @Before
    public void runBeforeEveryTestMethod(){
        System.out.println("@Before each method");
    }

    @Test
    public void testOne() {
        System.out.println("testOne testing");
    }

    @Test
    public void testTwo() {
        System.out.println("testTwo testing");
    }

}


运行上述测试将会打印:

@Before each method
testTwo testing
@Before each method
testOne testing

如果创建一个类继承上述的类,那么子类中的@Before方法会在测试方法之前父类的@Before执行之后执行:

public class BeforeTestExtend extends BeforeTest {
    
    @Before
    public void runBeforeEveryTestMethodAfterSuperClass(){
        System.out.println("@Before each method subClass");
    }
}

运行:

@Before each method
@Before each method subClass
testTwo testing
@Before each method
@Before each method subClass
testOne testing

与@Before相对的是@After注解。@After标注的方法将在每个@Test方法之后运行。如果我们在Before方法中使用了外部资源(如打开文件,数据库链接。。),则需要在测试运行后释放它们,这种情况下@After就可使用了。

@After用来标注一个public void方法,使得该方法在@Test方法之后运行。需要注意的是,即使@Before或@Test方法抛出异常,@After方法还是会保证运行。

但是如果是构造函数抛出异常,@After不会运行。

超类中声明的@After方法将在当前类之后运行。

public class AfterTest {
    File output;
    
    //Test Fixtures
    @Before
    public void setUp() throws Exception {
        output = new File("output");
        System.out.println("Before Start...");
    }
    @After
    public void tearDown() throws Exception {
        output.delete();
        System.out.println("After Starting...");
    }

    //Test Methods
    @Test
    public void testOne() {
        System.out.println("TestOne Runing!");
    }
    @Test
    public void testTwo() {
        System.out.println("TestTwo Runing!");
    }
}

执行测试,会打印:

Before Start...
TestOne Runing!
After Starting...
Before Start...
TestTwo Runing!
After Starting...

如果@Before方法里抛出了异常,@Test方法会跳过,但是@After还是会执行:

public class AfterTest {
    File output;
    
    //Test Fixtures
    @Before
    public void setUp() throws Exception {
        output = new File("output");
        System.out.println("Before Start...");
        throw new Exception();
    }
    @After
    public void tearDown() throws Exception {
        output.delete();
        System.out.println("After Starting...");
    }

    // Test Methods
    @Test
    public void testOne() {
        System.out.println("TestOne Runing!");
    }
    @Test
    public void testTwo() {
        System.out.println("TestTwo Runing!");
    }
}

运行测试会失败,但是@After会运行,将打印:

Before Start...
After Starting...
Before Start...
After Starting...
BeforeClass&AfterClass

@Before @After 是每个测试方法私有的环境设置。但是如果有多个测试共享同样的测试环境,特别是这些环境设置是比较昂贵的计算时,就没必要每个测试都设置,几个测试可以共享同样的测试Fixture,虽然这可能会影响测试的独立性,但有时候这是一个必要的优化。

在测试类以什么样的方式组织时就需要考虑到这一点。是一个对象对应一个测试类?还是一个业务场景对应一个测试类呢?一般来说比较好的做法是一个业务场景对应一个测试类,因为在同样的业务场景下,整个测试类共享测试Fixture的情况比较多,这样就可以使用后共享测试Fixture减少代码。

在Junit4中是使用@BeforeClass、@AfterClass的方法来实现共享Fixture。@BeforeClass、@AfterClass用来标注一个public static void no-arg方法,使得它在类中的所有测试方法运行之前/之后运行一次,也就是说如果你的测试类有十个测试,@Before/@After代码将被执行十次,但@BeforeClass/@AfterClass将只执行一次。超类的@BeforeClass方法将在当前类之前运行。一般来说,当多个测试需要共享相同的计算昂贵的设置代码时,就可以使用@BeforeClass。 比如建立数据库连接这一类。 当然可以不用@BeforeClass,可以将代码直接放到@Before,但是测试运行可能需要更花更多的时间。需要注意的是,标记@BeforeClass的代码是静态初始化运行的,因此它将在测试类的实例创建之前运行。由于是在测试类的实例创建之前,因此它会再@Before 方法之前运行,对应的@AfterClass会在@After之后运行。

public class ShareFixtureTest {

    // Test Fixture
    @BeforeClass
    public static void breforeTestOnlyOnce() throws Exception {
        System.out.println("Run before all test only once...");
        
    }
    @AfterClass
    public static void afterTestOnlyOnce() throws Exception {
        System.out.println("Run after all test only once...");
    }
    @Before
    public void beforePerTest(){
        System.out.println("Run before per test ...");

    }
    @After
    public void afterPerTest(){
        System.out.println("Run after per test ...");
    }
    
    //Test Method
    @Test
    public void testOne() {
        System.out.println("testOne Start...");
    }
    @Test
    public void testTwo() {
        System.out.println("testTwo Start...");
    }
}

打印:

Run before all test only once...
Run before per test ...
testOne Start...
Run after per test ...
Run before per test ...
testTwo Start...
Run after per test ...
Run after all test only once...

如果在测试的构造函数统计一下实例个数,可以发现,@BeforeClass在测试类的实例创建之前就开始运行了,以下代码仅做测试用:

public class ShareFixtureTest {
    
    private static int quantity = 0;
    
    public ShareFixtureTest() {
        quantity++;
    }

    //Test Fixtures
    @BeforeClass
    public static void breforeTestOnlyOnce() throws Exception {
        System.out.println("Run before all test only once..." + quantity);  
    }
    @AfterClass
    public static void afterTestOnlyOnce() throws Exception {
        System.out.println("Run after all test only once...");
    }

    //Test Methods
    @Test
    public void testOne() {
        System.out.println("testOne Start..." + quantity);
    }
    
    @Test
    public void testTwo() {
        System.out.println("testTwo Start..." + quantity);
    }
}

前面提过每个测试方法都会在单独的测试类的实例里面运行,实例就是会创建两个实例,但是@BeforeClass在测试实例创建之前就执行,所以打印0。

Run before all test only once...0
testOne Start...1
testTwo Start...2
Run after all test only once...

在JUnit 5中,标签@BeforeEach和@BeforeAll是JUnit 4中的@Before和@BeforeClass的等价物。它们的名称更多地表示了它们何时运行:“在每次测试之前”和“在所有测试之前”。

总结
注解 描述
@Before 非静态方法 void,(可以多次发生)来初始化测试方法级别的设置,每个@Test方法运行之前运行一次
@After 非静态方法 void,(可以多次发生)来清理测试方法级别的设置,每个@Test方法运行之后运行一次
@BeforeClass 静态方法 void 无参,用例来初始化测试类级别的设置,所有测试方法之前仅执行一次
@AfterClass 静态方法 void 无参,用例来初始化测试类级别的设置,所有测试方法之后仅执行一次

相应的执行流程,如下图:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容