1、模型設計
建立自動化用例,關鍵字模型。其中自動化用例基本內容包含title(目錄展示)、name等常見文字資訊,關鍵字則是實現自動化測試提速的關鍵所在,考慮到業務場景的自動化,就能發現有很多業務步驟是重複的:比如一個管理系統裡面的建立物件,我們將該步驟抽象出來,形成一個關鍵字(keyword),關鍵字儲存url、header等資訊,要錄入到自動化用例testcase模型中,則使用中間表TestCaseKeyword,它會詳細記錄某個自動化用例的(關鍵字-順序),並且關鍵字會帶有詳細的params、body等資訊,也就是可以被全域性變數,或者常量所覆蓋。
我們的project、testcase、keyword設計理念:首先project是用例的集合,每個project內包含name、title不能重複的testcase,至於project之間如果testcase name重複則無所謂。建立testcase,要帶上project資訊,用project_case來標記唯一。testcase一旦建立,就固定屬於某個project,沒必要對testcase做project級別的遷移。因為現在以projectname+testcasetitle唯一標記一個用例,那我們的update操作,就對project內的用例才生效,show_all也是展示同project的用例,search、delete_title_list同樣。
對於keyword和testcase,每個testcase以keyword+data+keyword順序來形成一套業務流程。keyword預設只存一些url、header-key,data-key等,在testcase建立、修改過程中,填入header-value、data-value等形成可用請求,填入order標記本keyword的順序。
斷言:是自動化用例來判斷是否透過的必要元素。斷言設計:給keyword新增斷言配置,在模型裡面應該是個斷言列表,每個元素是一個斷言,每個斷言由(目標值,比較符,被比較值組成),目標值、被比較值可以是int、string,比較符可以是數值型的大於、小於、等於、大於等於、小於等於,字元型的equal:相等、contains:被比較值包含目標值字串、in:目標值字串包含被比較值。 業務邏輯:1、keyword新增時,可以新增斷言到斷言列表,這個時候一個斷言元素的被比較值非必填項、但比較符、目標值都是必填元素;新增keyword也可以不必新增斷言。2、keyword修改時,同理,可以新增、修改、刪除斷言,但斷言元素的被比較值非必填項、但比較符、目標值都是必填元素; 3、新增、修改自動化用例的時候,可以給用例的keyword新增、修改、刪除斷言元素,這個時候斷言元素的被比較值可以填充了。
import functools import uuid import random import string from django.db import models from django.core.exceptions import ValidationError from django.utils import timezone # Create your models here. def generate_random_string(except_str, length=10): characters = string.ascii_letters + string.digits random_chars = ''.join(random.choice(characters) for _ in range(length)) return f'{except_str}_{random_chars}' if except_str else random_chars def validate_positive(value): if value < 0: raise ValidationError('%(value)s is not a positive integer or 0', params={'value': value}) class Project(models.Model): name = models.CharField(max_length=100, unique=True) description = models.TextField(blank=True, null=True) def __str__(self): return self.name class Testcase(models.Model): # 透過 related_name 訪問所有關聯的 Testcase 例項:testcases = project.testcases.all() project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='testcases') title_default = functools.partial(generate_random_string, "title") name_default = functools.partial(generate_random_string, "test") title = models.CharField(max_length=50, null=False, blank=False, default=title_default) name = models.CharField(max_length=50, null=False, blank=False, default=name_default) project_case = models.CharField(max_length=150, unique=True, null=False, blank=False) level = models.IntegerField(default=0, validators=[validate_positive]) precondition = models.CharField(max_length=300, null=True, blank=True, default=None) test_precondition = models.CharField(max_length=300, null=True, blank=True, default=None) expected_result = models.CharField(max_length=300, null=True, blank=True, default=None) TYPE = [ ("function_case", "功能用例"), ("performance_case", "效能用例"), ("reliability_case", "可靠性用例"), ] type = models.CharField(max_length=20, choices=TYPE, default="function_case") auto_flag = models.BooleanField(default=False, null=True, blank=True) description = models.TextField(blank=True, null=True) keywords = models.ManyToManyField("KeyWord", through='TestCaseKeyword') class Meta: # unique_together 確保在資料庫層面上欄位組合的唯一性約束。在 Testcase 模型中,它確保每個專案中的 title 和 name 的組合是唯一的 # 可以在資料庫層面上提供額外的安全保障 unique_together = ('project', 'title', 'name') def save(self, *args, **kwargs): # 在儲存物件到資料庫之前或之後執行一些操作。在 Testcase 模型中,重寫了 save 方法以自動生成 project_case 欄位的值 self.project_case = f"{self.project.name}_{self.title}" super(Testcase, self).save(*args, **kwargs) def __str__(self): return f"{self.title}_{self.name}" class KeyWord(models.Model): BODY_TYPES = [ ('application/x-www-form-urlencoded', 'Application/X-WWW-Form-Urlencoded'), ('raw', 'Raw'), ('multipart/form-data', 'Multipart/Form-Data'), ] name_default = functools.partial(generate_random_string, "kw") name = models.CharField(max_length=100, unique=True, null=False, blank=False, default=name_default) url = models.URLField() params = models.JSONField(blank=True, null=True) headers = models.JSONField(blank=True, null=True) body_type = models.CharField(max_length=50, choices=BODY_TYPES) body = models.TextField(blank=True, null=True) description = models.TextField(blank=True, null=True) def __str__(self): return self.name class TestCaseKeyword(models.Model): test_case = models.ForeignKey(Testcase, on_delete=models.CASCADE) keyword = models.ForeignKey(KeyWord, on_delete=models.CASCADE) order = models.PositiveIntegerField() params = models.JSONField(blank=True, null=True) headers = models.JSONField(blank=True, null=True) body = models.TextField(blank=True, null=True) class Meta: ordering = ['order'] def __str__(self): return f"{self.test_case.name} - {self.keyword.name} ({self.order})"
2、 業務分析與設計:
檢視操作
-
testcase檢視:
POST
請求,根據operate
欄位執行不同操作:create
: 建立測試用例。update
: 更新測試用例。delete
: 刪除測試用例。show_all
: 展示所有測試用例。search
: 搜尋測試用例。show_testcase
: 展示單個測試用例及其關聯的關鍵字。
-
project檢視:
POST
請求,根據operate
欄位執行不同操作:create
: 建立專案。update
: 更新專案。delete
: 刪除專案。show_all
: 展示所有專案。
關鍵函式分析
-
create_testcase:
- 建立測試用例並處理關聯的關鍵字。
- 關鍵字資訊儲存在
TestCaseKeyword
中間表中。
-
update_testcase:
- 更新測試用例及其關聯的關鍵字。
- 透過比較請求中的關鍵字與當前關鍵字,決定新增、修改或刪除。
-
delete_testcase:
- 刪除測試用例及其關聯的關鍵字。
- 級聯刪除透過
on_delete=models.CASCADE
實現。
-
show_all_testcases:
- 展示所有測試用例,按專案篩選。
-
search_testcase:
- 按
title
模糊搜尋測試用例。
- 按
-
show_testcase:
- 展示單個測試用例及其關聯的關鍵字。
-
create_project:
- 建立專案,使用序列化器驗證資料。
-
update_project:
- 更新專案,根據
update_source_name
查詢專案。
- 更新專案,根據
-
delete_project:
- 刪除專案,根據
delete_list
中的專案名稱批次刪除。
- 刪除專案,根據
-
show_all_project:
- 展示所有專案。
對於用例的keyword修改,大致邏輯:
-
建立
unchanged_keywords
列表:- 使用
unchanged_keywords
列表來儲存在current_keywords_dict
和request_keywords_dict
中都存在,並且值未發生變化的關鍵字。
- 使用
-
分類關鍵字:
to_delete_keywords
:請求中不存在的當前關鍵字。to_add_or_update_keywords
:請求中新增或修改的關鍵字。unchanged_keywords
:在當前關鍵字和請求關鍵字中都存在且未發生變化的關鍵字。
-
刪除不存在的關鍵字關聯:
- 使用
to_delete_keywords
列表刪除不存在的關鍵字關聯。
- 使用
-
新增或更新關鍵字:
- 使用
to_add_or_update_keywords
列表新增或更新關鍵字及其斷言。
- 使用
-
重新整理未改動關鍵字的斷言:
- 使用
unchanged_keywords
列表重新整理未改動關鍵字的斷言。
- 使用
對應請求體結構,統一設計為:
{ "operate": "create/update/delete/show_all/search", "project_name": "projectA", "parameters": { "title": "title", "name": "name", "level": "level", "precondition": "precondition", "test_precondition": "test_precondition", "expected_result": "expected_result", "type": "type", "auto_flag": "auto_flag", "update_source_title": "update_source_title", "delete_title_list": [], "description": "description", "keywords": [ { "name": "Example API Request", "order": 1, "params": {"param1": "value1_param1", "param2": "value1_param2"}, "headers": {"Authorization": "Bearer value1_token"}, "body": "value1_body" }, { "name": "keyword2", "order": 2, "params": {"param1": "value2_param1", "param2": "value2_param2"}, "headers": {"Authorization": "Bearer value2_token"}, "body": "value2_body" } ] } }
3、補充斷言設定:斷言既可在keyword建立時新增斷言,keyword修改時新增、修改、刪除斷言;也可在建立用例時,這個時候建立與keyword的聯絡、這個時候重新整理斷言;或者修改用例的時候,既修改了關鍵字,也同步了所有關鍵字的斷言。
tips:
1. python有前向引用:如果在一個模型類中引用了另一個尚未定義的模型類,可能會出現 Unresolved reference
錯誤。
可以在 Testcase
模型中使用前向引用,即在字串中引用 KeyWord
模型。這樣可以解決。但函式可以在程式碼的任何位置定義和呼叫。
前向引用:在欄位定義中使用字串形式的類名引用未定義的模型類。