unittest.TestCase中测试用例执行顺序问题

我们在做自动化测试用例的时候,通常会把一个个测试用例放到一个unittest.TestCase的扩展类中,当我们运行测试用例文件时,测试文件中的测试用例会随机执行的。如:

#-*- coding: UTF-8 -*-
import time
import unittest
import sys
 
class DemoTest(unittest.TestCase):
 
    def setUp(self):
        ….
Def test_a(self):
    ………
Def test_b(self):
    ………
Def test_c(self):
    ………
Def test_d(self):
    ………
Def tearDown(self):
    ………
if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(DemoTest)
    unittest.TextTestRunner(verbosity=2).run(suite)

上面的示例是包含四个测试用例的一个测试用例集,在我们执行这个测试文件后,四个测试用例的执行顺序是随机的。如果这几个测试用例有依赖关系,则会影响你们用例的执行,于是我在想能不能控制一下测试用例的执行顺序呢?虽然测试用例相互之间不能有依赖关系,可是这算是一个尝试吧!我在网上查到一个方法,是适用于junit的:
换种顺序来执行TestCase(Junit适用)
Junit的TestCase,总是按固定的顺序执行的. 正如你在Eclipse中跑Run As Junit Test, 无论你跑多少次, TestCase的执行顺序都是一致的,可重复的. 这就导致一个问题, TestCase之间的独立性无法保证.
例如下面一个Test类中的2个TestCase:

public class DaoTest {
 
    @Test
    public void test_count() {
        dao.insert(new User("root", "123456"));
        assertEquals(1, dao.count(User.class));
    }
 
    @Test
    public void test_insert() {
        dao.clear(User.class, null);
        dao.insert(new User("admin", "123456"));
        assertEquals(1, dao.count(User.class));
    }
 
}

如果先执行test_count()然后执行test_insert(),两个TestCase都能通过.
但如果先执行test_insert(),然后执行test_count(),则test_count()会失败.
所以,有必要去打乱TestCase的默认执行顺序,以暴露出TestCase本身的问题. TestCase更可靠,才能让主代码更可靠.
我实现了一个简单的方式,使用的是Junit的公开API, 测试过4.3和4.8.2,均可使用:
//得到所有带@Test的方法,这里用的是Nutz的资源扫描,反正你能得到全部Test类就行

        List list = Scans.me().scanPackage("org.nutz");
        List reqs = new ArrayList();
        Map reqMap = new HashMap();
        for (Class clazz : list) {
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                if (method.getAnnotation(Test.class) != null) {
                    //将单个TestCase(即一个Test Method),封装为Junit的Test Request
                    Request req = Request.method(clazz, method.getName());
                    reqs.add(req);
                    reqMap.put(req , method);//在最终打印测试结果时,方便查找具体出错的Method
                }
            }
        }
 
        // 因为reqs 是一个List,我们可以按需调整TestCase的顺序
        // 正序 //nothing change.
        // 反序Collections.reverse(reqs)
        // 乱序Collections.shuffle(reqs)
 
        //把执行顺序保存下来,方便重现执行顺序
        try {
            FileWriter fw = new FileWriter("./test_order.txt");
            for (Request request : reqs) {
                fw.write(reqMap.get(request).toString());
                fw.write("\n");
            }
            fw.flush();
            fw.close();
        }
        catch (IOException e) {}
 
        //到这里, List已经按我们预期的方式排好,可以执行测试了
        final TestResult result = new TestResult();
        RunNotifier notifier = new RunNotifier();
        notifier.addListener(new RunListener() { //需要设置一个RunListener,以便收集测试结果
 
            public void testFailure(Failure failure) throws Exception {
                result.addError(asTest(failure.getDescription()), failure.getException());
            }
            public void testFinished(Description description) throws Exception {
                result.endTest(asTest(description));
            }
            public void testStarted(Description description) throws Exception {
                result.startTest(asTest(description));
            }
 
            public junit.framework.Test asTest(Description description) {
                return new junit.framework.Test() {
 
                    public void run(TestResult result) {
                        throw Lang.noImplement();
                    }
 
                    public int countTestCases() {
                        return 1;
                    }
                };
            }
        });
        //来吧,执行之!!
        for (Request request : reqs) {
            request.getRunner().run(notifier);
        }
 
        //接下来,就是打印结果了.
        System.out.printf("Run %d , Fail %d , Error %d \n", result.runCount(), result.failureCount(), result.errorCount());
 
        if (result.failureCount() > 0) { //断言失败的TestCase
            Enumeration enu = result.failures();
            while (enu.hasMoreElements()) {
                TestFailure testFailure = (TestFailure) enu.nextElement();
                System.out.println("--Fail------------------------------------------------");
                System.out.println(testFailure.trace());
                testFailure.thrownException().printStackTrace(System.out);
            }
        }
 
        if (result.errorCount() > 0) { //抛异常的TestCase
            Enumeration enu = result.errors();
            while (enu.hasMoreElements()) {
                TestFailure testFailure = (TestFailure) enu.nextElement();
                System.out.println("--ERROR------------------------------------------------");
                System.out.println(testFailure.trace());
                testFailure.thrownException().printStackTrace(System.out);
            }
        }
        
        

来, 考验一下你的TestCase吧!! 让它在乱序中多次执行. Nutz按这种思路,已经爆出几个Bug(当然,我已经迅速fix了)
https://github.com/nutzam/nutz/blob/master/test/org/nutz/AdvancedTestAll.java
python中不好用,于是只好想一下其他的方法了。我想是不是可以把每个测试用例变成函数,然后再写一个函数来顺序调用它们呢?
这个方法虽然可以控制顺序,可以也有一定的风险,如果出错了,不会按测试用例给报错,不能准确地统计出测试用例地正确率!如果全部通过则没有问题,可是影响报告的结果,需要想办法来美化一下报告。
修改后的代码如下:

#-*- coding: UTF-8 -*-
import time
import unittest
import sys
 
class DemoTest(unittest.TestCase):
 
    def setUp(self):
        ….
Def a(self):
    ………
Def b(self):
    ………
Def c(self):
    ………
Def d(self):
    ………
Def  test_demo(self):
    a()
    b()
    c()
    d()
Def tearDown(self):
    ………
if __name__ == '__main__':
    suite = unittest.TestLoader().loadTestsFromTestCase(DemoTest)
    unittest.TextTestRunner(verbosity=2).run(suite)
    

http://blog.sina.com.cn/s/blog_68f262210102v6jv.html

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,579评论 18 139
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,558评论 18 399
  • 一. Java基础部分.................................................
    wy_sure阅读 3,784评论 0 11
  • Instrumentation介绍 Instrumentation是个什么东西? Instrumentation测...
    打不死的小强qz阅读 7,747评论 2 39
  • 标签(空格分隔): Android 单元测试的好处:Martin Fowler在《重构》里面还解释了为什么单元测试...
    背影杀手不太冷阅读 5,797评论 3 25