Python3+unitest自動化測試初探(上篇)


一、概念介紹

unit test:單元測試,能夠簡單粗暴地理解成用一段代碼去測試另一段代碼。unittest做爲Python單元測試框架之一,除了用來作單元測試以外,還能夠用來完成接口自動化,UI自動化(配合Selenium使用),自動化框架開發等。python

test fixture:測試用例執行前的準備工做以及測試用例執行完成後的清理工做。好比數據庫測試前要創建鏈接,測試後要關閉鏈接。mysql

test case:單元測試中最小的單元。web

test suite:測試套件是測試用例,測試套件或者二者的集合。一般被用來把測試用例組織起而後交給test runner執行。sql

test runner:測試執行器是執行用例並向用戶展現結果的組件。數據庫

unittest 官方文檔連接框架

二、準備工做

2.一、開發環境

  • 操做系統:Ubuntu 18.04.1 LTS
  • Python版本:3.7.0
  • 開發工具:PyCharm Edu
  • 本機已安裝MySQL
  • 代碼結構

MySQ安裝的連接函數

經過Python訪問MySQL數據庫的連接工具

2.二、建立數據庫和表

登陸數據庫建立數據庫ums,在數據庫中建立表user_info,SQL語句以下:單元測試

create database ums;

#status分爲active和inactive兩種
create table user_info(
id int(10)  primary key auto_increment,
name char(15) not null,
password char(100) not null,
status char(10) not null)
ENGINE=InnoDB DEFAULT CHARSET=utf8;

MySQ經常使用命令的連接

2.三、編寫簡單的註冊登陸代碼

在Project下新建Python包 userManage ,在該包下建立Python文件userRegLogin.py。註冊時先判斷是否存在狀態爲active的用戶,不存在則更新信息到數據庫中。登陸時先判斷用戶狀態,若是爲active則去數據庫中查詢其密碼並比較密碼是否正確。userRegLogin.py的代碼以下:

#coding:utf-8
#導入MySQL驅動
import   mysql.connector
import warnings

#定義類user_ange
class user_manage():
    #初始化 傳入兩個參數:用戶名和密碼
    def __init__(self,name,passwd):
        self.name = name
        self.passwd = passwd

    #執行select SQL語句並返回結果
    def execQuerySql(self,sql,arg):
        #臨時屏蔽告警ResourceWarning
        warnings.simplefilter("ignore", ResourceWarning)
        try:
            self.conn =  mysql.connector.connect(host="127.0.0.1",user='root',database='ums',password='password')
            self.cursor = self.conn.cursor()
            self.cursor.execute(sql,[arg])
            val = self.cursor.fetchone()[0]
            return val
        except Exception as e:
            print(e)
        finally:
            self.cursor.close()
            self.conn.close()


    #執行insert語句
    def execUpdateSql(self,sql,args):
        warnings.simplefilter("ignore", ResourceWarning)
        try:
            self.conn =  mysql.connector.connect(host="127.0.0.1",user='root',database='ums',password='password')
            self.cursor = self.conn.cursor()
            self.cursor.execute(sql,args)
            self.conn.commit()
        except Exception as  e:
            print(e)
        finally:
            self.cursor.close()
            self.conn.close

    #判斷用戶是否存在
    def userIsExist(self):
        sql1 = '''select count(*) from user_info where status='active' and name = %s'''
        userCount = self.execQuerySql(sql1,self.name)
        if userCount:
            return False
        else:
            return True

    #用戶註冊
    def userReg(self):
        lenFlag = len(self.passwd) >=6 and len(self.passwd)<=10
        #判斷是否存在同名用戶
        if self.userIsExist():
            #判斷密碼長度是否符合要求
            if lenFlag:
                sql2 = '''insert into user_info values (null,%s,%s,'active');'''
                # self.cursor.execute(sql2,[self.name,self.passwd])
                # self.conn.commit()
                args = [self.name,self.passwd]
                self.execUpdateSql(sql2,args)
                return "regSucess"
            else:
                return "passwordLenError"
        else:
            return "SameNameError"

    def isActive(self):
        sql3 = '''select status from user_info where  name=%s;'''
        # self.cursor.execute(sql3,[self.name])
        # ustatus = self.cursor.fetchone()[0]
        ustatus = self.execQuerySql(sql3,self.name)
        if ustatus == "active":
            return True
        else:
            return  False

    #用戶登陸
    def userLogin(self):
        '''
        用戶狀態爲active則校驗密碼是否正確
        反之則拋出異常
        '''
        if self.isActive():
            sql4 = '''select password from user_info where name=%s and status="active";'''
            pwdInDB = self.execQuerySql(sql4,self.name)
            if self.passwd == pwdInDB:

                return "loginSucess"
            else:
                return "passwordError"
        else:
            return "UserStatusError"

2.四、運行結果

在userRegLogin.py文件末尾插入代碼並執行

if __name__ == '__main__':
    #實例化
    user1 = user_manage("TestUser1","1234User1")
    user1.userReg()

登陸數據庫查看結果

mysql> select  *  from  user_info;
+----+-----------+-----------+--------+
| id | name      | password  | status |
+----+-----------+-----------+--------+
|  3 | TestUser1 | 1234User1 | active |
+----+-----------+-----------+--------+
1 row in set (0.00 sec)

點擊這裏返回目錄

2.五、測試場景

  1. 註冊時存在同名狀態爲active的用戶,返回SameNameError
  2. 註冊成功,返回regSucess
  3. 註冊密碼長度不符合要求,返回passwordLenError
  4. 登陸成功,返回loginSucess
  5. 登陸密碼不對,返回passwordError
  6. 登陸狀態爲inactive,返回UserStatusError

三、一個簡單的例子

在Project下新建Python包testCases,在testCases下新建Python文件userRegTest.py,用來編寫測試用戶註冊功能的代碼。
[示例1]:userRegTest.py

#coding:utf-8
#導入unittest模塊
import unittest
#從模塊userRegLogin中導入類user_manage
from userManage.userRegLogin import user_manage
#定義測試類,繼承於unittest.TestCase
class regTest(unittest.TestCase):
    #測試方法或者叫測試用例必須以test開頭
    #測試場景:密碼長度小於6
    def test_pwdlen(self):
        user2 = user_manage("TestUser4","1234")
        self.assertEqual(user2.userReg(),"passwordLenError")
    #測試場景:正常註冊
    def test_normalreg(self):
        user2 = user_manage("TestUser5","1234User")
        self.assertEqual(user2.userReg(),"regSucess")

#執行test開頭的方法
if __name__ == '__main__':
    unittest.main()

[示例1運行結果]:

  1. 測試類繼承與unittest.TestCase
  2. 一個測試用例就是Python中的一個函數。
  3. 測試用例必需要以test開頭

四、test fixture

test fixture包含兩個方法,setUp(self)和tearDown(self)分別用於執行測試用例前的準備工做和執行完測試用例後的清理工做。這個兩個方法會在每一個用例的執行前或執行後被調用。把準備和清理工做和測試用例放在會致使不少重複代碼且不易維護。test fixture的使用場景究竟是什麼樣的呢?

  1. 場景一:好比說用戶註冊時會先校驗數據庫中是否存在狀態爲active的同名用戶,那麼該用例執行過以後,須要清理user_info表中的記錄,這個動做就是在tearDown(self)方法中完成的,不然再次執行就會報錯。

  2. 場景二:要驗證同名用戶名沒法註冊的異常場景,須要先註冊一次或者手動在user_info表裏面插入數據,這個動做就是在setUp(self)中完成的。

    4.一、setUp和tearDown示例

    把被測類實例化放在setUp()f方法裏面。正常註冊場景的清理動做:刪除表user_info中對應的記錄放在tearDown()方法中。接下來完善一下示例1中的代碼:
    [示例2]:userRegTest.py
#coding:utf-8
#導入unittest模塊
import unittest
#從模塊userRegLogin中導入類user_manage
from userManage.userRegLogin import user_manage

#定義測試類,繼承於unittest.TestCase
class regTest(unittest.TestCase):
    #測試方法或者叫測試用例必須以test開頭
    #測試場景:密碼長度小於6
    def setUp(self):
        print("setUp run before test case")
        self.user1 = user_manage("TestUser1","1234")
        self.user2 = user_manage("TestUser2","TestUser2")
        self.user3 = user_manage("TestUser3","TestUser3")

        #註冊TestUser3
        self.user3.userReg()

    def test_pwdlenerr_L1(self):
        print("test case:test_pwdlenerr_L1")
        res = self.user1.userReg()
        self.assertEqual(res,"passwordLenError")

    #測試場景:正常註冊
    def test_regsucess_L0(self):
        print("test case:test_regsucess_L0")
        res = self.user2.userReg()
        self.assertEqual(res,"regSucess")

    #測試場景:用戶名重名
    def test_regagain_L1(self):
        print("test case:test_regagain_L1")
        res = self.user3.userReg()
        self.assertEqual(res,"SameNameError")

    def tearDown(self):
        print("tearDown run after test case")
        sql = '''delete from  user_info  where name = %s'''
        self.user2.execUpdateSql(sql,[self.user2.name])
        self.user3.execUpdateSql(sql,[self.user3.name])

if __name__ == '__main__':
    unittest.main()

[示例2運行結果]:

  1. 若是setUp()執行成功,那麼不論用例是否執行成功,tearDown()都會執行。
  2. 若是setUp()執行失敗,那麼用例及tearDown()方法都不會被執行。

若是隻想在全部用例執行以前只執行一次準備工做怎麼操做呢?那就須要用到setUpClass() 和 tearDownClass()了。在這兩個方法內部能夠本身編寫函數實現準備工做或清理動做。

4.二、setUpClass 和 tearDownClass

[示例3]:

#定義測試類,繼承於unittest.TestCase
class regTest(unittest.TestCase):
    #測試方法或者叫測試用例必須以test開頭
    #測試場景:密碼長度小於6
    @classmethod
    def setUpClass(cls):
        print("setUpClass run before test case")

    def test_pwdlen(self):
        print("test case:test_pwdlen")
        self.user1 = user_manage("TestUser8","1234")
        res = self.user1.userReg()
        self.assertEqual(res,"passwordLenError")

    #測試場景:正常註冊
    def test_normalreg(self):
        print("test case:test_normalreg")
        self.user2 = user_manage("TestUser10","123456")
        res = self.user2.userReg()
        self.assertEqual(res,"regSucess")

    @classmethod
    def tearDownClass(cls):
        # sql = '''delete from  user_info  where name = %s'''
        # sef.user2.execUpdateSql(sql,[self.user2.name])
        print("tearDownClass run after test case")
#執行test開頭的方法
if __name__ == '__main__':
    unittest.main()

注意:沒有清理動做若是想要用例跑成功的話須要手動刪除表裏對應的用戶信息或者修改註冊時傳入的name。
[示例3運行結果]::

點擊這裏返回目錄

五、測試套

5.一、登陸功能測試

在包testcases下新建Python文件userLoginTest.py,編寫測試登陸功能的代碼。
[ 示例4 ]:userLoginTest.py

#coding:utf-8
import unittest
from userManage.userRegLogin import user_manage

class loginTest(unittest.TestCase):
    #準備工做
    def setUp(self):
        self.user4 = user_manage("TestUser4","TestUser4")
        self.user5 = user_manage("TestUser4","TestUser5")
        self.user6 = user_manage("TestUser6","TestUser6")

        #驗證登陸功能前須要先註冊
        self.user4.userReg()
        #構造一個狀態爲inactive的用戶
        self.user6.userReg()
        setStatus = '''update user_info set status="inactive" where name=%s;'''
        self.user6.execUpdateSql(setStatus,[self.user6.name])
    #登陸成功測試
    def test_loginsucess_L0(self):
        res = self.user4.userLogin()
        self.assertEqual(res,"loginSucess")
    #密碼錯誤測試
    def test_pwdwrong_L0(self):
        res = self.user5.userLogin()
        self.assertEqual(res,"passwordError")
    #用戶狀態異常測試
    def test_statuserr_L1(self):
        res = self.user6.userLogin()
        self.assertEqual(res,"UserStatusError")
    #清理工做
    def tearDown(self):
        print("tearDown run after test case")
        #刪除用戶TestUser4,TestUser6
        sql = '''delete from  user_info  where name = %s'''
        self.user4.execUpdateSql(sql,[self.user4.name])
        self.user6.execUpdateSql(sql,[self.user6.name])

if __name__ == '__main__':
    unittest.main()

[ 示例4運行結果 ]:

5.二、組織用例

unittest經過類unittest.TestSuite來組織測試用例。這個類返回測試用例或測試套的集合,它能夠被test runner執行。運行一個測試套至關於test runner把測試套迭代,而後執行每個測試用例。 一些方法能夠將用例添加到測試套中。

  1. addTest(test):添加一個TestCase或TestSuite到套件中。
  2. addTests(tests):把TestCase和TestSuite中給的全部的測試實例添加到套件中。

TestSuite和TestCase都有以下方法:

  1. countTestCases():返回測試用例的數量。
  2. run(result):運行套件相關的測試用例,收集測試結果到result對象中並傳給result。

在Project下建立Python文件run.py,經過TestSuite來組織註冊登陸全部的用例並運行。
[ 示例5 ]:run.py

#coding:utf-8
import  unittest

#從testCase包裏面導入測試類
from testCases.userLoginTest import loginTest
from testCases.userRegTest import regTest

#構造測試套
def suite():
    suite = unittest.TestSuite()
    suite.addTest(loginTest("test_loginsucess_L0"))
    suite.addTest(loginTest("test_pwdwrong_L0"))
    suite.addTest(loginTest("test_statuserr_L1"))
    suite.addTest(regTest("test_pwdlenerr_L1"))
    suite.addTest(regTest("test_regsucess_L0"))
    suite.addTest(regTest("test_regagain_L1"))
    return suite

#運行測試用例
if __name__ == '__main__':
    runner = unittest.TextTestRunner()
    #調用test runner的run方法執行用例
    runner.run(suite())

[ 示例5運行結果 ]:

點擊這裏返回目錄

相關文章
相關標籤/搜索