自动化测试框架流程:

自动化测试:2 Unittest单元测试框架_测试用例

Unittest介绍

   unittest/PyUnit是从Java程序开发JUnit启发而来,unittest使我们具备创建测试用例、测试套件、测试夹具的能力。

unittest中最核心的四部分是:TestCase,TestSuite,TestRunner,TestFixture

  • 一个TestCase的实例就是一个测试用例。元测试(unittest)一个测试用例是一个完整的测试单元,通过运行这个测试单元,可以对某一个问题进行验证。

自动化测试:2 Unittest单元测试框架_用例_02

  • 而多个测试用例集合在一起,就是TestSuite,而且TestSuite也可以嵌套TestSuite。
  • TestLoader是用来加载TestCase到TestSuite中的。
  • TextTestRunner是来执行测试用例的,其中的run(test)会执行TestSuite/TestCase中的run(result)方法
  • 测试的结果会保存到TextTestResult实例中,包括运行了多少测试用例,成功了多少,失败了多少等信息。

•综上,整个流程就是首先要写好TestCase,然后由TestLoader加载TestCase到TestSuite,然后由TextTestRunner来运行TestSuite,运行的结果保存在TextTestResult中,整个过程集成在unittest.main模块中。

Unittest断言

方法

检查

assertEqual(a, b)

a ==b

assertNotEqual(a, b)

a !=b

assertTrue(x)

bool(x) is True

assertFalse(x)

Bool(x) is False

assertIs(a, b)

a is b

assertIsNot(a, b)

a is not b

assertIsNone(x)

x is None

assertIsNotNone(x)

x is not None

assertIn(a, b)

a in b

assertNotIn(a, b)

a not in b

assertIsInstance(a, b)

isinstance(a,b)

assertNotIsInstance(a, b)

not isinstance(a,b)

下面实例代码如下:

#python自带存在断言语句

#语法:assert  表达式,[参数]   参数是基于如果断言失败的话则会返回的错误信息提示
#assert 2!=2,"2不等于2"
class  MyException(Exception):
    def __init__(self,message):
        self.message=message


def raise_ex(number):
    if number==10:
        raise MyException("该数不能等于10")




import sys
import unittest
class  Assert_Test(unittest.TestCase):
    def test_1(self):
        #断言表达式的结果是False时则断言通过,非False则断言失败
        #self.assertFalse(1==1)
        #self.assertIn(10,[3202,312])  #if 10 in []:
        #在测试用例中的框架中是不会存在判定的,通过使用断言完成的
        #判定两个对象的内存地址
        #self.assertIs([1,2],[1,2])
        #主要是测试两个数计算减法后是否无限接近0,可以通过places进行确定位数取值
        #self.assertAlmostEqual(2,1.8999,places=0)
        #self.assertIsNot()  #与上面的is对应,判定两个不是相同的对象地址
        #self.assertListEqual([1,2,3],[2,3,1])  #可以直接传入两个列表对象进行比较;实现的每个元素依次顺序进行比较
        #self.assertDictEqual()
        #异常如何断言:分析:需要考虑两部分内容断言:断言异常对象;断言异常内容
        # try:
        #     raise_ex(10)    #断言异常
        # except:
            #需要获取到异常对象和异常信息
            # get_ex_res=sys.exc_info()
            # get_ex_obj=get_ex_res[0]
            # get_ex_msg=get_ex_res[1]  #获取的是一个MyException的字符串对象需要通过str进行转换
            # self.assertIs(MyException,get_ex_obj)
            # self.assertEqual("该数不能等于10",str(get_ex_msg))
            #如果针对异常断言,实际unittest框架给我们提供了更佳的断言方法
        #如果使用下面语句的话,则不能够使用try....except捕获异常
        with self.assertRaises(Exception) as ex:
            #会抛出异常的代码
            raise_ex(10)
        #如果异常对象比较是通过的,则下面就可以比较异常信息,可以通过返回的ex对象中的exception属性进行调用异常信息
        #但是同样的返回的是一个MyException异常信息的对象,需要转换成字符串对象进行比较
        self.assertEqual("该数不能等于10",str(ex.exception))

采用unittest完成组织执行测试用例,示例如下:

# 此模块主要是用于进行设计测试用例执行的;单元测试框架:unittest
# 单元测试框架:不同的语言具有不同的单元测试框架,在Python中主流的测试框架是unittet、pytest,unittest是基础也是python的内置模块
# unittest框架主要包括核心四部分:test fixture、test case、test suite、test runner
# 如果需要设计一个测试类的话,那么该类必须继承unittest框架中的testcase类;那么该类运行时则会以unittest.testcase中的运行器进行运行;否则使用的
# 是python运行器运行
import unittest
from C14.CRMProject.Business_Object.Login_Business import LoginBusiness
from C14.CRMProject.Data_Object.DataBase_Data.Conn_Crm import ConnCrm
from C14.CRMProject.Data_Object.File_Data.Login_Data.Read_Login_Data import ReadLoginData

# 类名最佳实践要求:通常情况下,类名名会与对应的被测试类名前加+Test或者后+Test
# 声明一个全局变量数据库连接对象
connect = None


class LoginTest(unittest.TestCase):
    get_yaml_data = None

    # connect1=None
    # 执行顺序是:setUp---->测试用例1---->tearDown--->setUp---->测试用例2---->tearDown---->>>>
    # testcase类存在的setup和teardown方法
    # 以上方法名都是固定的因为是继承于TestCase,对该类中的两个方法将进行重写
    def setUp(self):  # 在每个测试用例执行之前会先执行setUp方法,
        self.login = LoginBusiness("http://192.168.141.100:7878/index.php/login")

    # 操作完之后需要退出浏览器
    def tearDown(self):
        self.login.get_driver.quit()

    # 数据库连接资源整个所有测试过程中只需要建立连接一次,最后所有用例执行完毕之后关闭一次即可,没有必要在每个测试用例中进行定义
    @classmethod
    def setUpClass(cls):  # 在所有用例执行之前进行创建数据库连接对象,并通过全局变量进行获取
        global connect
        connect = ConnCrm()
        # cls.connect1=ConnCrm()
        cls.get_yaml_data = ReadLoginData().read_data()

    @classmethod
    def tearDownClass(cls):  # 在所有的测试用例执行完毕之后进行关闭对象
        connect.get_cursor.close()
        connect.get_conn.close()

    # 如何设计一个用例,此测试用例是测试登陆成功的
    # 用例规则要求:用例名必须以test开头,通常命名会使用test+对应需要测试的方法名;否则声明在该类中其他所方法为普通方法不是一个测试用例方法
    def test_login_success(self):
        get_param = LoginTest.get_yaml_data["login_success_1"]
        # *login_success_1:
        self.login.click_login(get_param[0], get_param[1])
        # 完成两部分校验:数据库、页面
        get = connect.check_password(get_param[0], get_param[1])
        self.assertTrue(get[0], get[1])  # 此处如何断言通过还是不通过

    # 用户名为空
    def test_login_fail_1(self):
        get_param = LoginTest.get_yaml_data["login_fail_1"]
        self.login.click_login(get_param[0], get_param[1])
        print(self.login.login_error_message())
        # assert login.login_error_message()=="用户名不能够为空","用户名为空测试用例失败"
        # 使用unittest框架所带有的断言方法;第一个first参数传入的是Expectd,第二个second参数传入的是actual
        self.assertEqual("用户名和密码不能为空", self.login.login_error_message(), "预期与实际不一致")

    # 密码为空的情况
    def test_login_fail_2(self):
        get_param = LoginTest.get_yaml_data["login_fail_2"]
        self.login.click_login(get_param[0], get_param[1])
        print(self.login.login_error_message())
        # assert login.login_error_message()=="密码不能够为空","密码为空测试用例失败"
        self.assertEqual("用户名和密码不能为空", self.login.login_error_message(), "预期与实际不一致")

    # 以上三条用例只是单纯 的执行了登陆的业务流。并没有进行判定是登陆成功还是登陆失败或者登陆失败的提示是否正确  # username=""  password="admin888"
    # 预期结果值:登陆失败,并提示:用户名不能够为空  # username="admin"  password=""     预期结果值:登陆失败,并提示:密码不能够为空  # username="admin"
    # password="admin888"


if __name__ == '__main__':
    # unittest框架也封装了main函数;如果将鼠标放于指定的某一个用例上右键运行得好话则只会执行那一个用例,如果需要执行所有用例
    # 则将鼠标置于main函数中或者置于类上运行;unittest框架中的测试用例执行顺序并不是从上往下的,其用例执行顺序是根据方法名的首字母的ascII码值依次执行
    unittest.main()

# unittest框架运行结果具有三种状态:pass(通过)、error(错误:表示的是程序员人为所犯的错,基于当前的测试代码都存在语法或者规则错误问题)、
# fail(失败):表示的是预期结果与实际结果断言不一致的结果;
# 断言:在python实际存在断言语句:assert;在实际测试过程中,必须添加断言,没有断言的用例是不完整的

# testfixture:测试固件:是实现整个测试中的所有测试用例的所有资源管理操作(包括测试用例的前置资源、后置清除资源操作等);实现资源的
# 资源的销毁等动作;


在封装的代码以及示例代码中都有每行的注释,大家可以根据每个注释进行理解每行代码的意义,如有疑惑,可以随时咨询并与本人进行技术交流,谢谢!~