Django测试本地正常但GitHub Actions执行失败求助
Django测试用例本地正常但GitHub Actions未触发DataError的问题
我有2个Django测试用例在本地运行完全正常,但在GitHub Actions中执行失败(Python 3.9版本)。具体是测试名称过长和CUIT过长导致创建失败的用例,本地能正常触发DataError,但GitHub Actions中报错提示DataError not raised。
依赖文件(requirements.txt)
Django==4.1.7 django-crum==0.7.9 django-widget-tweaks==1.4.12 et-xmlfile==1.0.1 flake8==5.0.4 openpyxl==3.0.10 pandas==1.5.1 Pillow==9.3.0 python-dotenv==0.19.0 XlsxWriter==1.1.5 xmltodict==0.13.0
模型代码
class Empresa(models.Model): name = models.CharField(max_length=120, verbose_name='Razon Social') cuit = models.CharField(max_length=11, validators=[MinLengthValidator(11)]) user = models.ForeignKey(User, on_delete=models.CASCADE) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) def __str__(self) -> str: return self.name def save(self, force_insert=False, force_update=False, using=None, update_fields=None): if not self.user: user = get_current_user() if user and not user.pk: user = None if not self.pk: self.user = user self.user = user return super().save(force_insert, force_update, using, update_fields) def toJSON(self): item = model_to_dict(self) return item
测试用例说明
- 成功创建Empresa实例:测试正常通过
- 名称过长导致创建失败:本地正常,GitHub Actions中失败
- CUIT过长导致创建失败:本地正常,GitHub Actions中失败
测试代码
class EmpresaTesting(TestCase): def setUp(self): self.client = Client() self.user = User.objects.create(username='testuser', password='12345') self.user.save() def test_create_empresa_ok(self): this_empresa = Empresa.objects.create(name='Empresa 1', cuit='30999999991', user=self.user) self.assertTrue(isinstance(this_empresa, Empresa)) self.assertEqual(str(this_empresa), 'Empresa 1') def test_create_empresa_long_name(self): long_name = "a" * 121 with self.assertRaisesMessage(DataError, "Data too long for column 'name' at row 1"): Empresa.objects.create(name=long_name, cuit='30999999991', user=self.user) def test_create_empresa_long_cuit(self): with self.assertRaisesMessage(DataError, "Data too long for column 'cuit' at row 1"): Empresa.objects.create(name="Empresa 1", cuit='123456789012', user=self.user)
GitHub Actions报错信息
FAIL: test_create_empresa_long_cuit (export_lsd.tests.EmpresaTesting) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/runner/work/el_conta/el_conta/export_lsd/tests.py", line 28, in test_create_empresa_long_cuit Empresa.objects.create(name="Empresa 1", cuit='123456789012', user=self.user) File "/opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/contextlib.py", line 126, in __exit__ next(self.gen) File "/opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/django/test/testcases.py", line 876, in _assert_raises_or_warns_cm yield cm AssertionError: DataError not raised ====================================================================== FAIL: test_create_empresa_long_name (export_lsd.tests.EmpresaTesting) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/runner/work/el_conta/el_conta/export_lsd/tests.py", line 24, in test_create_empresa_long_name Empresa.objects.create(name=long_name, cuit='30999999991', user=self.user) File "/opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/contextlib.py", line 126, in __exit__ next(self.gen) File "/opt/hostedtoolcache/Python/3.9.16/x64/lib/python3.9/site-packages/django/test/testcases.py", line 876, in _assert_raises_or_warns_cm yield cm AssertionError: DataError not raised
GitHub Actions工作流配置
name: Django CI on: push: branches: [ "develop" ] pull_request: branches: [ "develop" ] jobs: build: runs-on: ubuntu-latest strategy: max-parallel: 4 matrix: python-version: [3.8, 3.9] steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install Dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt - name: Check pending migrations run: | python manage.py makemigrations --check --dry-run python manage.py migrate - name: Lint with Flake8 run: | flake8 --count --show-source --max-complexity=10 --max-line-length=130 --statistics --ignore=C901,F811 - name: Run Tests run: | python manage.py test
问题原因与解决方法
核心原因
本地和GitHub Actions使用的数据库行为不一致:
- 本地大概率使用MySQL/PostgreSQL这类严格模式的数据库,当字段长度超出限制时会直接抛出
DataError。 - GitHub Actions默认使用SQLite,SQLite的默认策略是自动截断过长的字符串而不是抛出错误,这就导致测试用例中的
assertRaisesMessage无法捕获到预期的异常。
解决方案
方案1:修改测试用例,依赖Django ORM层验证(推荐)
不要依赖数据库层面的错误,而是在模型中添加自定义验证逻辑,在ORM层拦截非法数据,这样跨数据库环境都能稳定运行。
- 给
Empresa模型添加clean()方法:
from django.core.exceptions import ValidationError class Empresa(models.Model): # 原有字段定义... def clean(self): # 验证名称长度 if len(self.name) > 120: raise ValidationError({"name": "名称长度不能超过120个字符"}) # 验证CUIT长度 if len(self.cuit) != 11: raise ValidationError({"cuit": "CUIT必须是11位字符"})
- 修改测试用例,触发ORM验证并捕获
ValidationError:
def test_create_empresa_long_name(self): long_name = "a" * 121 with self.assertRaisesMessage(ValidationError, "名称长度不能超过120个字符"): empresa = Empresa(name=long_name, cuit='30999999991', user=self.user) empresa.full_clean() # 触发模型的clean验证 empresa.save() def test_create_empresa_long_cuit(self): with self.assertRaisesMessage(ValidationError, "CUIT必须是11位字符"): empresa = Empresa(name="Empresa 1", cuit='123456789012', user=self.user) empresa.full_clean() empresa.save()
方案2:在GitHub Actions中使用和本地一致的数据库
如果必须依赖数据库层面的错误,可以在GitHub Actions中配置和本地相同的数据库(比如MySQL/PostgreSQL)。
以MySQL为例,修改工作流配置:
jobs: build: runs-on: ubuntu-latest strategy: max-parallel: 4 matrix: python-version: [3.8, 3.9] # 添加MySQL服务 services: mysql: image: mysql:8.0 env: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: test_db ports: - 3306:3306 options: >- --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=5 steps: - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - name: Install Dependencies run: | python -m pip install --upgrade pip pip install -r requirements.txt mysqlclient # 安装MySQL驱动 - name: Configure Database run: | # 设置环境变量,让Django连接MySQL echo "DATABASE_NAME=test_db" >> $GITHUB_ENV echo "DATABASE_USER=root" >> $GITHUB_ENV echo "DATABASE_PASSWORD=root" >> $GITHUB_ENV echo "DATABASE_HOST=127.0.0.1" >> $GITHUB_ENV echo "DATABASE_PORT=3306" >> $GITHUB_ENV - name: Check pending migrations run: | python manage.py makemigrations --check --dry-run python manage.py migrate # 后续lint、测试步骤不变...
同时修改项目的settings.py,根据环境变量配置数据库:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': os.getenv('DATABASE_NAME', 'local_db'), 'USER': os.getenv('DATABASE_USER', 'root'), 'PASSWORD': os.getenv('DATABASE_PASSWORD', ''), 'HOST': os.getenv('DATABASE_HOST', '127.0.0.1'), 'PORT': os.getenv('DATABASE_PORT', '3306'), 'OPTIONS': { 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", # 开启严格模式 }, } }
内容的提问来源于stack exchange,提问作者Eugenio
相关产品推荐
相关产品推荐

