数据驱动

       数据驱动从数据文件读取输入数据,通过变量的参数化将测试数据传入测试脚本,不同的数据文件对应不同的测试用例,数据和脚本分离。数据来控制测试的业务流。比如你测一个WEB程序,有很多页面,你可以通过一个数据来控制每次是再哪个页面下工作的(即通过数据来导航到相应的页面)。它是关键字驱动的低级版本,他控制的是函数级的,而关键字是控制动作级的。所以数据驱动应该是可以控制整个测试的。

Unittest框架参数化

       如果一个业务需要通过多组数据进行完成测试的话,那么一个测试类中肯定会涉及多个测试用例,且每个测试用例会存在重复的操作,那么此时肯定是效率不佳且走读性不好;当然此时肯定会有同学思考到使用循环,但是如何通过循环进行控制对象创建,如何控制用例执行呢?所以肯定不能够使用循环解决,必然此时需要借助unittest框架中的参数化完成。

       在unittest框架中并没有直接提供参数化的实现方式,而是需要通过第三方模块进行完成;

A.ParamUnittest模块:该模块主要实现unittest框架的参数化,并且是装饰在类上;

  • 缺点:不能够单独针对其中某一个测试方法进行装饰参数化,在实现参数化时必须只定义一个测试方法;
  • 代码示例如下:
import unittest
import paramunittest


@paramunittest.parametrized(
    ('1', '2'),
    #(4, 3),
    ('2', '3'),
    (('4', ), {'b': '5'}),
    ((), {'a': 5, 'b': 6}),
    {'a': 5, 'b': 6},
)
class TestFoo(paramunittest.ParametrizedTestCase):
    def setParameters(self, a, b):
        self.a = a
        self.b = b


    def testLess(self):
        self.assertLess(self.a, self.b)


@paramunittest.parametrized(
    ('1', '2'),
    #(4, 3),
    ('2', '3'),
    (('4', ), {'b': '5'}),
    ((), {'a': 5, 'b': 6}),
    {'a': 5, 'b': 6},
)
class TestBar(unittest.TestCase):
    def setParameters(self, a, b):
        self.a = a
        self.b = b


    def testLess(self):
        self.assertLess(self.a, self.b)

B.Parameterized模块:可以应用于多个自动化测试框架:nose框架、unittest框架、pytest框架;该模块不仅可以用于进行装饰类参数化,还可以装饰方法参数化,且还可以装饰函数参数化;后期的pytest框架的参数化也是需要使用该模块完成;

  • 如果是函数参数化的话则直接使用parameterized装饰器
  • 如果是方法参数化的话则需要使用parameterized调用expand方法进行装饰
  • 如果是类参数化的话则需要使用parameterized_class装饰器
  • 示例代码如下:
from nose.tools import assert_equal
from parameterized import parameterized, parameterized_class


import unittest
import math


@parameterized([
    (2, 2, 4),
    (2, 3, 8),
    (1, 9, 1),
    (0, 9, 0),
])
def test_pow(base, exponent, expected):
   assert_equal(math.pow(base, exponent), expected)


class TestMathUnitTest(unittest.TestCase):
   @parameterized.expand([
       ("negative", -1.5, -2.0),
       ("integer", 1, 1.0),
       ("large fraction", 1.6, 1),
   ])
   def test_floor(self, name, input, expected):
       assert_equal(math.floor(input), expected)


@parameterized_class(('a', 'b', 'expected_sum', 'expected_product'), [
   (1, 2, 3, 2),
   (5, 5, 10, 25),
])
class TestMathClass(unittest.TestCase):
   def test_add(self):
      assert_equal(self.a + self.b, self.expected_sum)


   def test_multiply(self):
      assert_equal(self.a * self.b, self.expected_product)


@parameterized_class([
   { "a": 3, "expected": 2 },
   { "b": 5, "expected": -4 },
])
class TestMathClassDict(unittest.TestCase):
   a = 1
   b = 1


   def test_subtract(self):
      assert_equal(self.a - self.b, self.expected)

C.DDT模块(Data Driven Test数据驱动测试):表示的是可以实现多种类型参数化的数据结构传入(前面的两种形式主要是以容器型的数据类型传入为主,可以装饰在类、函数、方法上); 

  • a.单一型数据结构:无论传入的数据是何种类型,都当做一个值处理; 
  • b.复合数据类型结构处理(list、tuple、dict):如果需要针对这些类型的数据分别对应参数的话则必须添加一个装饰器,@ddt.unpack    
  • c.可以直接读取数据格式文件中的数据进行参数化;
  • 示例代码如下:
@ddt
class FooTestCase(unittest.TestCase):
    def test_undecorated(self):
        self.assertTrue(larger_than_two(24))


    @data(3, 4, 12, 23)
    def test_larger_than_two(self, value):
        self.assertTrue(larger_than_two(value))


    @data(1, -3, 2, 0)
    def test_not_larger_than_two(self, value):
        self.assertFalse(larger_than_two(value))


    @data(annotated(2, 1), annotated(10, 5))
    def test_greater(self, value):
        a, b = value
        self.assertGreater(a, b)


    @data(annotated2([2, 1], 'Test_case_1', """Test docstring 1"""),
          annotated2([10, 5], 'Test_case_2', """Test docstring 2"""))
    def test_greater_with_name_docstring(self, value):
        a, b = value
        self.assertGreater(a, b)
        self.assertIsNotNone(getattr(value, "__name__"))
        self.assertIsNotNone(getattr(value, "__doc__"))


    @file_data('data/test_data_dict_dict.json')
    def test_file_data_json_dict_dict(self, start, end, value):
        self.assertLess(start, end)
        self.assertLess(value, end)
        self.assertGreater(value, start)


    @file_data('data/test_data_dict.json')
    def test_file_data_json_dict(self, value):
        self.assertTrue(has_three_elements(value))


    @file_data('data/test_data_list.json')
    def test_file_data_json_list(self, value):
        self.assertTrue(is_a_greeting(value))


    @needs_yaml
    @file_data('data/test_data_dict_dict.yaml')
    def test_file_data_yaml_dict_dict(self, start, end, value):
        self.assertLess(start, end)
        self.assertLess(value, end)
        self.assertGreater(value, start)


    @needs_yaml
    @file_data('data/test_data_dict.yaml')
    def test_file_data_yaml_dict(self, value):
        self.assertTrue(has_three_elements(value))


    @needs_yaml
    @file_data('data/test_data_list.yaml')
    def test_file_data_yaml_list(self, value):
        self.assertTrue(is_a_greeting(value))


    @data((3, 2), (4, 3), (5, 3))
    @unpack
    def test_tuples_extracted_into_arguments(self, first_value, second_value):
        self.assertTrue(first_value > second_value)


    @data([3, 2], [4, 3], [5, 3])
    @unpack
    def test_list_extracted_into_arguments(self, first_value, second_value):
        self.assertTrue(first_value > second_value)


    @unpack
    @data({'first': 1, 'second': 3, 'third': 2},
          {'first': 4, 'second': 6, 'third': 5})
    def test_dicts_extracted_into_kwargs(self, first, second, third):
        self.assertTrue(first < third < second)


    @data(u'ascii', u'non-ascii-\N{SNOWMAN}')
    def test_unicode(self, value):
        self.assertIn(value, (u'ascii', u'non-ascii-\N{SNOWMAN}'))


    @data(3, 4, 12, 23)
    def test_larger_than_two_with_doc(self, value):
        """Larger than two with value {0}"""
        self.assertTrue(larger_than_two(value))


    @data(3, 4, 12, 23)
    def test_doc_missing_args(self, value):
        """Missing args with value {0} and {1}"""
        self.assertTrue(larger_than_two(value))


    @data(3, 4, 12, 23)
    def test_doc_missing_kargs(self, value):
        """Missing kargs with value {value} {value2}"""
        self.assertTrue(larger_than_two(value))


    @data([3, 2], [4, 3], [5, 3])
    @unpack
    def test_list_extracted_with_doc(self, first_value, second_value):
        """Extract into args with first value {} and second value {}"""
        self.assertTrue(first_value > second_value)

以上示例均来源于官网,所以大家一定要学会如何查找对应模块的API,学会分析其模块中的示例。

最后CRM系统封装的三大类型参数化的代码,部分截图:

python自动化测试unittest:4 数据驱动模式_Test

python自动化测试unittest:4 数据驱动模式_数据_02