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

发表于:2018-2-23 09:36

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:望月成三人    来源:51Testing软件测试网采编

  我们在做自动化测试用例的时候,通常会把一个个测试用例放到一个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):
  ………
  ifname == 'main':
  suite = unittest.TestLoader().loadTestsFromTestCase(DemoTest)
  unittest.TextTestRunner(verbosity=2).run(suite)


上文内容不用于商业目的,如涉及知识产权问题,请权利人联系博为峰小编(021-64471599-8017),我们将立即处理。
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号