1. 程式人生 > >學習版pytest核心測試平臺開發萬字長文入門篇

學習版pytest核心測試平臺開發萬字長文入門篇

# 前言 2021年,測試平臺如雨後春筍般冒了出來,我就是其中一員,寫了一款pytest核心測試平臺,在公司落地。分享出來後,有同學覺得挺不錯,希望能開源,本著“公司程式碼不要傳到網上去,以免引起不必要麻煩”的原則,只能在家從頭寫一個,邊重新梳理程式碼邊溫習鞏固知識點,以學習交流為目的,定義為“學習版”。 # 功能展示 ## 登入 ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091253800-897011064.png) ## 介面自動化 介面自動化--Dashboard: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091254535-1279736382.png) 介面自動化--環境變數: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091254766-355505610.png) 介面自動化--fixtures: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091254986-262829213.png) 介面自動化--用例管理: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091255239-1284896245.png) 介面自動化--用例管理--編輯用例: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091255567-840499425.png) 介面自動化--測試計劃: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091255880-493273367.png) 介面自動化--語法說明: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091256121-2128714421.png) ## 小工具 小工具--共享指令碼: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091256398-1956859424.png) 小工具--HTTP狀態查詢: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091256742-1248557618.png) ## 後臺管理 後臺管理--使用者管理 ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091257162-1158063318.png) 後臺管理--專案管理 ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091257401-997677759.png) # 本文開發內容 - 登入,登出 - 首頁,修改密碼,個人資訊 - 後臺管理,使用者管理 - JWT認證 本文先打個基礎,既是測試平臺基本結構,也可以作為CMS基礎框架,定製開發各種小型專案。 # 技術棧 - Node.js 12.16.3 - Vue 4.5.11 - Python 3.8 - Django 3.1.3 - Django REST framework 3.12.2 - SQLite 3 IDE編輯器推薦PyCharm旗艦版,既能寫Django也能寫Vue專案。資料庫使用Django自帶SQLite ,省去安裝MySQL和Navicat/Workbench麻煩,輕量級開發。SQLiteStudio為SQLite資料庫視覺化工具,只需要下載即可,無需安裝,解壓就用:: https://sqlitestudio.pl/ ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091257621-1261076144.png) 由於會用到`models.JSONField`,SQLite預設不相容,所以需要下載`sqlite3.dll`檔案替換下: https://www.sqlite.org/download.html 根據Python版本選擇,比如我的windows安裝的Python38-32,下載了`sqlite-dll-win32-x86-3340100.zip`這個軟體包,解壓後將`D:\Program Files (x86)\Python38-32\DLLs\sqlite3.dll`替換。 # 建立Vue專案 設定`npm`淘寶映象: ```shell npm config set registry https://registry.npm.taobao.org ``` 安裝`Vue CLI`: ```shell npm install -g @vue/cli ``` 建立`teprunner-frontend`專案: ```shell vue create teprunner-frontend ``` > 專案名字請隨意。 預設選項點選回車進行建立: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091257802-216205447.png) # 編寫Vue程式碼 新增靜態資源: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091258174-2093927824.png) 包括css樣式、字型樣式、圖示、logo。 > 推薦一個圖示下載網站:https://www.easyicon.net/。 編輯`package.json`,安裝專案所需依賴: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091258364-972562148.png) `axios`用於非同步請求,傳送`http`給後端。`element-ui`為餓了麼開源前端框架,簡化了從頭寫html麻煩,高度複用,統一風格。`vue-router`提供了路由跳轉,在上個時代,路由是在後端來控制的,把頁面渲染後返回給前端直接展示,前後端分離後,後端只負責返回資料,把控制權交給前端。 `devDependencies`是寫程式碼用到的依賴,這裡把`eslint`和`prettier`標出來了,它們是用來做程式碼靜態檢查的,配置後能給與程式碼規範提示,幫你寫出更漂亮的程式碼,同樣是在`package.json`檔案編輯: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091258573-750991233.png) 接著執行`npm install`進行安裝。有可能會出現下圖提示: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091258759-287817120.png) 執行`npm audit fix`就修復好了: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091258942-28200737.png) 新建`vue.config.js`檔案,新增Vue專案配置: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091259182-765432005.png) `args[0].title`給網頁設定了瀏覽器title。`proxy`指定了後端介面根路徑為`/api`,後端伺服器訪問地址為`http://127.0.0.1:8000/`,這是Django啟動後預設本地域名和埠。`element-ui`預設頁面是會出現滾動條的,在登入頁會顯得很醜,需要在`public/index.html`加上樣式: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091259440-281235541.png) Vue程式執行入口是`main.js`,把需要初始化載入的程式碼寫在這裡: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091259659-1274997218.png) app會掛載到`index.html`檔案中`div`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091259875-907416163.png) 這是整個Vue專案唯一的`html`檔案,其他元件都是掛載到這個`div`下面的。其中有個`App.vue`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091300052-1598689769.png) 它叫做根元件,`router-view`是一塊區域,用來展示路由匹配到的元件,也就是說所有路由匹配到的元件都會通過`App.vue`根元件來展示。路由配置在`router/index.js`檔案中編輯: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091300376-2127888544.png) 第一層路由是`/login`登入和`/`首頁,首頁只有選單,沒有具體內容,顯示沒有意義,所以重定向到了後臺管理的使用者管理。第二層路由是具體的功能模組,作為子路由放在首頁路由下,比如後臺管理。後臺管理的子模組使用者管理也放到了後臺管理的子路由,根據`url`訪問路徑定義父子路由關係。 為了在未登入的情況下,不允許訪問首頁,需要再加上訪問攔截: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091300615-713843562.png) 同時添加了`meta.requireAuth`,用來設定哪些路由需要攔截,這裡把首頁設定為`True`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091300803-207100786.png) 登入就不需要了。路由配置完成了,接著編寫頁面程式碼,Vue專案的頁面只有`index.html`一個`html`檔案,其他頁面都是放在`views`資料夾下,新建一個`views/login/index.vue`檔案: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091301015-629339053.png) 使用`el-form`標籤新增使用者名稱、密碼、忘記密碼和登入按鈕。`:model`給表單綁定了資料物件,分別填充到`form.username`、`form.password`、`form.rememberMe`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091301259-2052613775.png) `:rules`定義了表單規則,比如是否必填: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091301437-947986639.png) > 登入沒有做使用者名稱和密碼校驗,新增使用者時才會做校驗。 在建立登入介面時,從`localStorage`中移除`userInfo`和`token`,登入資訊保留7天: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091301641-790378134.png) 點選登入按鈕會呼叫`login`方法,發起登入請求: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091301875-210592429.png) 新建`views/home/index.vue`,編寫首頁程式碼: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091302187-1272491916.png) ``提供了連結跳轉,左上角logo跳轉到首頁,頂部導航欄根據後端返回的`authList`許可權選單進行顯示,因為後臺管理只有管理員才能訪問。接著編寫右上角區域程式碼: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091302405-1711410196.png) 包括修改密碼、個人資訊和退出登入,為了簡單一點,沒有弄頭像了。修改密碼使用`el-dialog`做了個彈出框: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091302691-679200967.png) 包括當前密碼、新密碼、確認新密碼。並添加了校驗規則: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091302928-1916970739.png) 修改密碼會呼叫`/users/passwords/set`介面: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091303224-752784567.png) 同時初始化選單許可權,從後端獲取`authList`,並判斷是否有許可權,沒有許可權的話跳轉到登入頁面: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091303410-1794239802.png) 首頁除了左上角logo,頂部導航欄,右上角個人資訊,還有一個重要的版塊就是左側選單。由於有了頂部導航欄,左側選單如果也放到首頁來寫,由於層級關係會讓程式碼顯得很臃腫,所以選單是放到每個子模組來做的。每個子模組有左側選單,也會存在很多重複容易的程式碼,為了複用,就抽成元件,放到`components`資料夾下: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091303622-1672694460.png) 用到了`el-menu`標籤: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091303825-340061116.png) `slot`是個插槽,相當於挖個坑在這,用的時候填一下坑,類似於模板。然後用`el-breadcrumb`做了個麵包屑,點選可跳轉到相應路由。接著就把左側選單應用到後臺管理模組上,新建`views/console/index.vue`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091304034-1338395360.png) 左側選單搞定了,右側內容也是類似的,查詢、表格、分頁、增刪改查,也需要抽成元件: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091304250-240544090.png) ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091304573-1692660628.png) 再新建`views/console/userManagement.vue`,編寫使用者管理程式碼: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091304818-1323270096.png) 用到了`el-form`和`el-table`標籤。表格資料通過`:data`繫結到了`tableData`物件,呼叫後端介面後,從響應中拿資料填充: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091305085-1579172335.png) 新增使用者彈窗的入口也是放在這個檔案中的: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091305271-1981573343.png) `dialogFormVisible`預設為`False`不可見,點選新增按鈕後,會設定為`True`。新建`views/console/addUser.vue`檔案編寫使用者彈窗的程式碼: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091305473-1808297284.png) 使用者管理`userManagement.vue`和新增使用者`addUser.vue`這兩個元件叫做父子元件,父元件如果想傳值給子元件,需要通過`props`來實現: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091305692-1166425773.png) `watch`能監視傳值的狀態,及時渲染。 > `watch`不是必須的,等到做編輯用例和用例執行結果的時候,會更加體會到它的作用。 新增使用者時,會對使用者名稱和密碼做校驗: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091305905-284602853.png) `nameValidator`和`pwdValidator`是公共方法,定義在`utils/const.js`檔案中: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091306121-1631706792.png) `utils`資料夾下還有個`commonMethods.js`檔案,定義了一些公共`js`方法: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091306330-1685495346.png) 本次前端程式碼基本編寫完成了: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091306538-1306986780.png) 最後還有個`axios.js`,它定義了非同步請求例項: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091306734-187641787.png) 添加了一個請求攔截器: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091306919-1338752669.png) 校驗`header`需要包括`jwt`請求頭:`Authorization: Bearer `。還添加了一個響應攔截器: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091307126-383883353.png) 對錯誤資訊進行捕獲並彈框提示。 # 建立Django專案 安裝Django: ```shell pip install --default-timeout=6000 -i https://pypi.tuna.tsinghua.edu.cn/simple django ``` 建立`teprunner-backend`專案: ```shell django-admin startproject teprunnerbackend ``` > 專案名字請隨意。 注意這條命令的專案名字不能帶短橫線`-`,如果想用短橫線,可以先去掉短橫線執行命令,再手動改回來,外層這個名字對專案沒有任何影響: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091307313-779882750.png) # 編寫Django程式碼 安裝依賴包: ```shell pip install --default-timeout=6000 -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt ``` ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091307538-931828764.png) 建立`user`應用: ```shell django-admin startapp user ``` ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091307720-1169917494.png) 配置`teprunnerbackend/settings.py`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091307900-1274811398.png) `django-cors-headers`為Django提供了跨域訪問的解決方案,需要配置`ALLOWED_HOSTS`為`*`,允許所有域訪問,並註冊`INSTALLED_APPS`和`MIDDLEWARE`。`user`應用也需要在`INSTALLED_APPS`註冊後才會生效。繼續改配置,把時區改為`Asia/Shanghai`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091308107-1374803092.png) 繼續: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091308319-84079789.png) Django自帶了一個許可權管理系統,為了簡單一點,直接複用。不過需要對`user`表進行自定義改造,所以通過配置裡面的`AUTH_USER_MODEL`指定為剛剛建立的`user`應用的`User`。`REST_FRAMEWORK`是Django RESTful framework的配置項,同樣要進行自定義改造,所以這裡通過配置`DEFAULT_AUTHENTICATION_CLASSES`指定認證鑑權類為`CustomJSONWebTokenAuthentication`,通過`EXCEPTION_HANDLER`指定異常處理函式為`custom_exception_handler`,通過`DEFAULT_PAGINATION_CLASS`指定分頁類為`CustomPagination`。`JWT_AUTH`是`jwt`的配置項,定義了過期時間為30天,允許重新整理,重新整理間隔,響應處理,`header`字首。最後補充了`django-cors-headers`的3個配置。 接著配置`teprunnerbackend/urls.py`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091308518-677420817.png) 把`user`的`url`都新增到`api/users/`下面。新建`user/urls.py`檔案: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091308716-1454224076.png) 分別新增登入、使用者增刪改查、重置密碼、角色列表、修改密碼幾個路徑。Django的檢視有兩個型別:類檢視和函式檢視。`path()`只接受可呼叫物件,所以類檢視需要使用`as_view()`進行轉化,比如`views.UserLogin.as_view()`。函式檢視直接寫上函式名就可以了,比如`views.update_password`。 開啟`user/models.py`檔案,新增資料模型: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091308923-881169454.png) `model`建立了程式碼和資料庫的對映,這稱為`orm`,物件關係對映。基礎表定義了共有的`created_at`和`updated_at`欄位。`auto_now_add`表示記錄新增時間,`auto_now`表示記錄更新時間,都是自動進行,無需手動寫程式碼來處理。使用者表繼承了Django自帶的`AbstractUser`,`REQUIRED_FIELDS`規定了哪些欄位必填,`username`和`password`是隱式規定了必填的,不需要設定,預設`email`也是必填,這裡把它去掉。 > Django預設表名會加上`django_`字首,使用`Meta.db_table`來定義沒有字首的表名。 `model`寫完了,執行以下命令同步到資料庫中,建立表結構: ```shell python manage.py makemigrations python manage.py migrate ``` 開啟`SQLiteStudio`,選擇根目錄的`db.sqlite3`檔案: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091309249-2017978542.png) 看到表結構已經建立好了: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091309420-747053000.png) `Role`有個`models.JSONField`欄位,為`選單許可權JSON`,使用Django的`fixtures`給專案新增初始化資料: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091309604-1337550531.png) `fixtures`名字是固定的,就像pytest的`conftest.py`一樣,只認這個名字。`user.json`存放資料: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091309804-987084195.png) 包括管理員使用者、角色許可權、管理員角色對照關係。其中角色許可權資料共3條: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091310014-2003867057.png) `auth`裡面定義了選單,對應首頁的頂部導航欄的欄目,比如本文只添加了後臺管理。`access`表示角色是否有許可權訪問,只有管理員的這條資料,`access`為`true`。通過以下命令把這些資料寫入資料庫中: ```shell python manage.py loaddata user ``` > Django會在`user.fixtures`目錄下自動找名字為`user`的`.json`、`.xml`或`.yaml`檔案進行載入。 接著新建一個`user/serializers.py`檔案寫序列化的程式碼。Django序列化是指,把資料庫的資料轉化為`json`返回給前端,反序列化是指把前端傳過來的`json`寫入資料庫。先寫登入的序列化器: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091310212-1706574966.png) 繼承自`serializers.ModelSerializer`,一般需要在`Meta`定義兩個屬性,`model`指定相應的模型,`fields`指定所需要的的欄位,這些欄位就是`json`的`key`。圖中的`roleName`不屬於`User`表的欄位,所以通過定義為`serializers.SerializerMethodField()`,再定義`get_roleName()`方法來取。`serializer`寫好後,寫檢視,編輯`user/views.py`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091310457-1238785660.png) 由於是`jwt`認證,所以這裡繼承了`JSONWebTokenAPIView`,提取請求引數,`check_password()`簡單校驗了下請求的密碼和資料庫的密碼`hash`值是否相等,後面的程式碼是`JSONWebTokenAPIView.post`方法現成的,重寫了一遍。`ErrInvalidPassword`等是在`user/errors.py`定義的錯誤響應: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091310834-1521640587.png) > 這樣可讀性會更高。響應狀態碼也建議這麼寫`status=status.HTTP_500_INTERNAL_SERVER_ERROR`,`from rest_framework import status`已經定義好了所有狀態碼的常量。 新建`user/utils.py`檔案,編寫`jwt_response_payload_handler`來定義登入介面的響應結構: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091311063-1495954193.png) 返回`token`、`user`、`auth`三個欄位。`custom_exception_handler`規範了一下異常響應資訊。這2個方法都是在`settings.py`中的`REST_FRAMEWORK`配置過的,還有一項配置是分頁,新建`user/pagination.py`檔案: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091311314-854451602.png) 繼承了`PageNumberPagination`,指定了查詢引數名`page`、`perPage`,自定義了響應欄位名`currentPage`、`items`、`totalNum`、`totalPage`,並添加了2個欄位`hasNext`和`hasPrev`。 最後還有一個配置項,自定義認證鑑權,新增`user/authentications.py`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091311551-1219438932.png) 繼承了`BaseJSONWebTokenAuthentication`。通過`get_authorization_header`提取請求頭中的`Authorization`欄位,沒有就提示“缺失JWT請求頭”。後面的程式碼是父類現成的。 至此,整個後端基本程式碼都寫完了,`jwt`認證也做好了: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091311830-445422217.png) 後面的程式碼就集中在`serializers.py`和`views.py`兩個檔案,序列化器提供資料庫表字段和響應`json`的序列化和反序列化,檢視使用序列化器,編寫業務處理程式碼。按照這個思路,編寫使用者的增刪改查功能,先在`serializers.py`檔案中寫2個序列化器`UserCreateSerializer`和`UserPagingSerializer`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091312017-1850050628.png) 由於新增使用者和使用者列表展示的欄位不一樣,所以給同一個`User`模型建立了2個序列化器。圖中標紅了程式碼是把`int`的`id`值轉化為了`str`型別,方便前端處理。`is_staff`表示是否為管理員,這個名字是Django定的。再寫`views.py`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091312214-1920783861.png) `GenericViewSet`是Django REST framework提供了超級封裝的類檢視,一般不需要重寫,給`queryset`和`serializer_class`賦值就可以了。不過因為有些自定義規則,所以本專案進行了複寫。`permission_classes`指定了介面訪問許可權,`IsAdminUser`表示必須管理員才能訪問,也是Django定義好的,和前面的`is_staff`相對應: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091312405-1969003977.png) 類似的,我在`user/permissions.py`新建了個`IsTester`,用來控制某些功能只能測試使用: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091312625-1711474494.png) > 本文還用不到這個。 重寫查詢使用者列表`list`方法: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091312852-381298251.png) 增加`username`和`nickname`的模糊查詢。 重寫新增使用者`create`方法: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091313109-1559669160.png) 首先寫`user`表,根據角色名是否包含管理員,判斷是否寫`is_staff`欄位,接著用入庫後產生的`user_id`寫`user_role`表。注意最後一行的`status`,新增的話,狀態碼返回`201`。 重寫修改使用者的`put`方法: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091313368-393021359.png) 和新增使用者的區別在於,更新`user_role`表資料時,需要根據老角色和新角色,比較差異後,新增新增的,刪除廢舊的。 重寫刪除使用者的`delete`方法: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091313613-199933718.png) 同時刪除`user`表和`user_role`表。注意最後一行的`status`,刪除的話,狀態碼返回`204`。 另外還自定義了`user_detail`方法,返回單個使用者資訊: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091313909-283826952.png) `GenericViewSet`的這些請求方法在`user/urls.py`檔案中配置對映關係: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091314130-1847703476.png) ``定義了`url`中的整形引數,`pk`為變數名,通過`kwargs["pk"]`來取。 在新增使用者的時候,需要從角色列表中選擇角色,需要後端提供這樣的介面,使用`ListAPIView`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091314313-652727999.png) 4行程式碼搞定一個介面,這就是Django的好處,除了`ListAPIView`,還有`CreateAPIView`、`RetrieveAPIView`、`ListCreateAPIView`等,按需取用。 密碼重置介面用`APIView`來實現: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091314503-1457448909.png) 定義了`put`方法,從請求`url`中獲取引數值`user_id`,查詢`user`物件後,呼叫預置的`set_password`方法,把密碼重置為`qa123456`。記得呼叫`user.save()`把資料更新到資料庫。 除了類檢視,Django也提供了函式檢視,並且Django REST framework提供了函式檢視的方法裝飾器,可以像`flask`框架一樣,感受寫純後端介面的體驗,按這個方法來寫修改密碼介面: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091314763-1042855761.png) `@api_view(['PUT'])`是Django REST framework提供的方法裝飾器。修改密碼時,會對`jwt`進行解碼,獲取到`user_id`,然後檢查老密碼是否和資料庫中的密碼`hash`值一致。 # 前後端聯調 根據以上思路把前後端的程式碼寫完以後,就可以把專案跑起來看看效果了。先啟動Django專案: ```shell python manage.py runserver ``` 接著啟動Vue專案: ```shell npm run serve ``` 訪問: http://localhost:8080/ 就能看到登入頁面了,預設超管使用者名稱為`admin`,密碼為`qa123456`,登入成功後可以嘗試走一遍功能檢查下: 1. 點選左上角logo,不會出現跳轉到空白頁。 2. 點選右上角資訊,彈出下拉選單,分別有修改密碼、個人資訊、退出登入。 3. 點選退出,返回登入頁,重新登入。 4. 查詢右上角個人資訊,包括使用者名稱、暱稱、角色。 5. 通過右上角下拉選單修改密碼,和老密碼不匹配會提示修改失敗,填寫正確資訊會修改成功,自動跳轉到登入頁面重新登入。輸入老密碼登入失敗,輸入新密碼登入成功。 6. 新增使用者,保持預設密碼,新增成功後,用`qa123456`登入成功。 7. 新增使用者,選擇自定義密碼,新增成功後,用`qa123456`登入失敗,用自定義密碼登入成功。 8. 新增使用者,分別建立管理員、開發、測試3個角色使用者。 9. 使用新使用者登入,管理員使用者能登入成功,開發和測試由於沒有後臺管理許可權,點選登入介面後會提示“無選單許可權”。 10. 修改使用者,修改使用者名稱、密碼,修改測試角色使用者為管理員角色,重新登入,能看到使用者名稱、密碼已更新為修改後的使用者名稱、密碼,並且管理員角色生效,能登進去看到後臺管理功能。 11. 輸入使用者名稱或暱稱,點選搜尋按鈕,測試模糊查詢功能正常,重置後清空搜尋框,自動查詢一次列表。 12. 點選刪除按鈕,提示是否確認刪除,確認後刪除成功,檢查資料庫`user_role`表資料也被清理乾淨。 13. 切換分頁,重新整理列表,選擇不同分頁條數,正常計算顯示相應的分頁總數。 14. 找到自定義密碼的使用者,點選重置密碼,重置成功後,重新登入,使用自定義密碼登入失敗,使用預設密碼`qa123456`登入成功。 15. 點選左側選單旁邊的麵包屑按鈕,能收起和展開左側選單。 > 由於時間關係,目前還沒有做角色管理功能,角色通過後端Django的`fixtures/user.json`進行資料初始化。 # Postman搭建Mock Server 在寫前端程式碼過程中,後端還沒有寫好,可以找一個服務模擬後端,提供響應資料進行前端除錯,這項技術叫做Mock,這個服務稱為Mock Server。Mock Server可以使用JavaScript的`mock.js`來搭建,也可以使用Python的`FastAPI `,不嫌麻煩用`Spring Boot`或者`Nginx`做一個也可以。不過為了方便起見,選擇上手就能用的可以省不少心。一些網站會提供線上Mock服務,在網站上填寫`url`和`response body`,有個缺點是我找了一圈都沒有發現能設定響應狀態碼的,比如在除錯`axios.js`的響應攔截器時,就需要根據`404`、`500`來進行除錯,看效果。尋尋覓覓,發現平時都在用的Postman,直接可以做Mock Server。首先登陸Postman,只有登陸後才能使用這個功能: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091314974-1585804704.png) 可以選擇用Google賬號登陸,也可以註冊一個。點選左上角`+ New`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091315201-1930518181.png) 選擇`Mock Server`。依次填寫請求方法、請求路徑、響應狀態碼、響應體: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091315613-50414724.png) 點選表格右上角的三個點還能新增請求體和介面描述: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091315832-796092045.png) 接著點選下一步: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091316030-441241306.png) 填寫Mock Server的名字為`hello`,其他選項預設,點選`Create Mock Server`進行建立: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091316264-2043994100.png) 關閉後,Mock Server就建好了: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091316509-681914679.png) 接著從左側`Collections`中找到這個介面,點選開啟: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091316713-1568476379.png) 此時還不能傳送請求,需要在右上角選擇環境`hello`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091316896-182901555.png) 傳送請求成功: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091317101-1406273875.png) 其中`url`是隱藏了的,點選右上角環境旁邊的眼睛圖示檢視: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091317330-236306263.png) 修改已建立介面mock資料的入口在`Examples`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091317759-481067449.png) 點選`Default`: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091318013-2026865732.png) 提供了新增時更直觀的操作介面,比如我把響應狀態碼改成了`404`,響應體改成了`{"msg": "hello not found"}`,點選右上角`Save Example`儲存後,再次請求: ![](https://img2020.cnblogs.com/blog/1629545/202103/1629545-20210307091318236-154884900.png) 實際mock的狀態碼和響應體也更新了。 # 小結 本文是學習版pytest核心測試平臺開發的入門篇,內容比較充實,全文字數上萬,一共截了103張圖,藉助`FastStone Capture`這個小工具,還算輕鬆,希望能讓讀者更直觀的看到平臺功能和程式碼邏輯。“編寫Vue程式碼”和“編寫Django程式碼”2個小節的內容是一氣呵成寫完的,沒有做修改,怕改了之後反而影響行文思路,不夠暢快。前端專案參考了一些開源專案如`Tcloud`、`FasterRunner`等,把程式碼看懂後,自己重新組織了程式碼和規範,在除錯過程中,也學會了寫Vue,做學習版`teprunner`時就從頭寫了一遍。後端程式碼完全是我自己寫的,先學了一遍Django和Django REST framework官方教程,其中《Django認證系統並不雞肋反而很重要》這篇文章在騰訊雲+社群2020年度徵文活動中,被評選為了最受喜愛作者獎,如果對Django認證系統不是很清楚的話,可以看看。最後,個人水平有限,有錯誤或不足之處,還請見諒。雖然測試平臺不一定能完全落地,但是做一遍開發對能力的提升是極大的。`teprunner`並不算優秀平臺,和真正企業級專案比起來,就是個小玩具。如果能博君一樂,也算是體現價值了。 > 參考資料: > > 原始碼前端 https://github.com/dongfanger/teprunner-frontend > > 原始碼後端 https://github.com/dongfanger/teprunner-backend > > 個人部落格 https://dongfanger.gitee.i