首页 > 解决方案 > 测试 django 应用程序时 github 工作流失败

问题描述

我创建了一个 django rest api,并且正在设置一个仅执行 linting 和测试的 github 操作。

我使用 pytest、pytest-django 和 pytest-cov 在本地运行测试,所有测试都通过了。Django 创建虚拟数据库并测试 api。

当我运行 github 操作时出现错误(见下文),我认为这是由于 github 环境内部没有数据库。

这是 github 操作:

name: Django CI

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

jobs:
  build:

runs-on: ubuntu-latest
strategy:
  max-parallel: 4
  matrix:
    python-version: [3.7, 3.8, 3.9]
services:
  db:
    image: postgres:12.3-alpine
    env:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
      POSTGRES_DB: github_actions
    ports:
      - 5432:5432
    options: --mount type=tmpfs,destination=/var/lib/postgresql/data --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
  uses: actions/setup-python@v2
  with:
    python-version: ${{ matrix.python-version }}
- name: psycopg2 prerequisites
  run: sudo apt-get install libpq-dev
- name: Install Dependencies
  run: |
    python -m pip install --upgrade pip
    pip install flake8 pytest
    if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Run migrations
  run: python django_api-project/manage.py migrate
  env:
      SYSTEM_ENV: GITHUB_WORKFLOW
      DJANGO_SETTINGS_MODULE: django_api.settings
- name: Lint with flake8
  run: |
    # stop the build if there are Python syntax errors or undefined names
    flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
    # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
    flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Run Tests
  run: |
    pytest django_api-project -s -v --durations=0 --cov=. -m "not skip_in_ci"
  env:
      SYSTEM_ENV: GITHUB_WORKFLOW
      DJANGO_SETTINGS_MODULE: django_api.settings    
      

以下是执行 pytest 步骤时出现的错误。

你知道这里有什么问题吗?

    Run pipenv run  pytest django_api-project/api -s -v --durations=0 --cov=. -m "not skip_in_ci"
============================= test session starts ==============================
platform linux -- Python 3.7.11, pytest-6.2.5, py-1.10.0, pluggy-1.0.0 -- /home/runner/.local/share/virtualenvs/django-restAPI-5iRsuPs6/bin/python
cachedir: .pytest_cache
django: settings: django_api.settings (from ini)
rootdir: /home/runner/work/django-restAPI/django-restAPI, configfile: pytest.ini
plugins: flake8-1.0.7, cov-2.12.1, django-4.4.0
collecting ... collected 7 items

django_api-project/api/tests/test_api.py::test_zero_companies_should_return_empty_list Operations to perform:
  Synchronize unmigrated apps: messages, rest_framework, staticfiles
  Apply all migrations: admin, auth, contenttypes, sessions
Running pre-migrate handlers for application admin
Running pre-migrate handlers for application auth
Running pre-migrate handlers for application contenttypes
Running pre-migrate handlers for application sessions
Running pre-migrate handlers for application api
Synchronizing apps without migrations:
  Creating tables...
    Running deferred SQL...
Running migrations:
  Applying contenttypes.0001_initial... OK (0.006s)
  Applying auth.0001_initial... OK (0.018s)
  Applying admin.0001_initial... OK (0.011s)
  Applying admin.0002_logentry_remove_auto_add... OK (0.013s)
  Applying admin.0003_logentry_add_action_flag_choices... OK (0.012s)
  Applying contenttypes.0002_remove_content_type_name... OK (0.027s)
  Applying auth.0002_alter_permission_name_max_length... OK (0.033s)
  Applying auth.0003_alter_user_email_max_length... OK (0.013s)
  Applying auth.0004_alter_user_username_opts... OK (0.009s)
  Applying auth.0005_alter_user_last_login_null... OK (0.014s)
  Applying auth.0006_require_contenttypes_0002... OK (0.001s)
  Applying auth.0007_alter_validators_add_error_messages... OK (0.011s)
  Applying auth.0008_alter_user_username_max_length... OK (0.013s)
  Applying auth.0009_alter_user_last_name_max_length... OK (0.014s)
  Applying auth.0010_alter_group_name_max_length... OK (0.016s)
  Applying auth.0011_update_proxy_permissions... OK (0.010s)
  Applying auth.0012_alter_user_first_name_max_length... OK (0.012s)
Creating test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...
  Applying sessions.0001_initial... OK (0.003s)
Running post-migrate handlers for application admin
Adding content type 'admin | logentry'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Running post-migrate handlers for application auth
Adding content type 'auth | permission'
Adding content type 'auth | group'
Adding content type 'auth | user'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Running post-migrate handlers for application contenttypes
Adding content type 'contenttypes | contenttype'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Running post-migrate handlers for application sessions
Adding content type 'sessions | session'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Adding permission 'Permission object (None)'
Running post-migrate handlers for application api
ERROR
django_api-project/api/tests/test_api.py::test_one_company_success ERROR
django_api-project/api/tests/test_api.py::test_empty_company ERROR
django_api-project/api/tests/test_api.py::test_company_already_exist ERROR
django_api-project/api/tests/test_api.py::test_create_new_company ERROR
django_api-project/api/tests/test_api.py::test_create_layoff_company ERROR
django_api-project/api/tests/test_api.py::test_create_company_wrong_status ERROR

==================================== ERRORS ====================================
________ ERROR at setup of test_zero_companies_should_return_empty_list ________

self = <django.db.backends.utils.CursorWrapper object at 0x7f8c808dee10>
sql = 'SELECT "api_company"."id", "api_company"."name", "api_company"."status", "api_company"."last_update", "api_company"."application_link", "api_company"."notes" FROM "api_company" ORDER BY "api_company"."id" ASC'
params = ()
ignored_wrapper_args = (False, {'connection': <django.db.backends.sqlite3.base.DatabaseWrapper object at 0x7f8c81462850>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7f8c808dee10>})

    def _execute(self, sql, params, *ignored_wrapper_args):
        self.db.validate_no_broken_transaction()
        with self.db.wrap_database_errors:
            if params is None:
                # params default might be backend specific.
                return self.cursor.execute(sql)
            else:
>               return self.cursor.execute(sql, params)

../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/utils.py:84: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.backends.sqlite3.base.SQLiteCursorWrapper object at 0x7f8c808e1eb0>
query = 'SELECT "api_company"."id", "api_company"."name", "api_company"."status", "api_company"."last_update", "api_company"."application_link", "api_company"."notes" FROM "api_company" ORDER BY "api_company"."id" ASC'
params = ()

    def execute(self, query, params=None):
        if params is None:
            return Database.Cursor.execute(self, query)
        query = self.convert_query(query)
>       return Database.Cursor.execute(self, query, params)
E       sqlite3.OperationalError: no such table: api_company

../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/sqlite3/base.py:423: OperationalError

The above exception was the direct cause of the following exception:

request = <SubRequest '_django_db_marker' for <Function test_zero_companies_should_return_empty_list>>

    @pytest.fixture(autouse=True)
    def _django_db_marker(request) -> None:
        """Implement the django_db marker, internal to pytest-django.
    
        This will dynamically request the ``db``, ``transactional_db`` or
        ``django_db_reset_sequences`` fixtures as required by the django_db marker.
        """
        marker = request.node.get_closest_marker("django_db")
        if marker:
            transaction, reset_sequences, databases = validate_django_db(marker)
    
            # TODO: Use pytest Store (item.store) once that's stable.
            request.node._pytest_django_databases = databases
    
            if reset_sequences:
                request.getfixturevalue("django_db_reset_sequences")
            elif transaction:
                request.getfixturevalue("transactional_db")
            else:
>               request.getfixturevalue("db")

../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/pytest_django/plugin.py:470: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/pytest_django/fixtures.py:120: in django_db_setup
    **setup_databases_args
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/test/utils.py:183: in setup_databases
    serialize=connection.settings_dict['TEST'].get('SERIALIZE', True),
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/base/creation.py:90: in create_test_db
    self.connection._test_serialized_contents = self.serialize_db_to_string()
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/base/creation.py:136: in serialize_db_to_string
    serializers.serialize("json", get_objects(), indent=None, stream=out)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/core/serializers/__init__.py:129: in serialize
    s.serialize(queryset, **options)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/core/serializers/base.py:90: in serialize
    for count, obj in enumerate(queryset, start=1):
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/base/creation.py:133: in get_objects
    yield from queryset.iterator()
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/models/query.py:353: in _iterator
    yield from self._iterable_class(self, chunked_fetch=use_chunked_fetch, chunk_size=chunk_size)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/models/query.py:51: in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/models/sql/compiler.py:1175: in execute_sql
    cursor.execute(sql, params)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/utils.py:66: in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/utils.py:75: in _execute_with_wrappers
    return executor(sql, params, many, context)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/utils.py:84: in _execute
    return self.cursor.execute(sql, params)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/utils.py:90: in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/utils.py:84: in _execute
    return self.cursor.execute(sql, params)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.backends.sqlite3.base.SQLiteCursorWrapper object at 0x7f8c808e1eb0>
query = 'SELECT "api_company"."id", "api_company"."name", "api_company"."status", "api_company"."last_update", "api_company"."application_link", "api_company"."notes" FROM "api_company" ORDER BY "api_company"."id" ASC'
params = ()

    def execute(self, query, params=None):
        if params is None:
            return Database.Cursor.execute(self, query)
        query = self.convert_query(query)
>       return Database.Cursor.execute(self, query, params)
E       django.db.utils.OperationalError: no such table: api_company

../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/sqlite3/base.py:423: OperationalError
__________________ ERROR at setup of test_one_company_success __________________

self = <django.db.backends.utils.CursorWrapper object at 0x7f8c808dee10>
sql = 'SELECT "api_company"."id", "api_company"."name", "api_company"."status", "api_company"."last_update", "api_company"."application_link", "api_company"."notes" FROM "api_company" ORDER BY "api_company"."id" ASC'
params = ()
ignored_wrapper_args = (False, {'connection': <django.db.backends.sqlite3.base.DatabaseWrapper object at 0x7f8c81462850>, 'cursor': <django.db.backends.utils.CursorWrapper object at 0x7f8c808dee10>})

    def _execute(self, sql, params, *ignored_wrapper_args):
        self.db.validate_no_broken_transaction()
        with self.db.wrap_database_errors:
            if params is None:
                # params default might be backend specific.
                return self.cursor.execute(sql)
            else:
>               return self.cursor.execute(sql, params)

../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/utils.py:84: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <django.db.backends.sqlite3.base.SQLiteCursorWrapper object at 0x7f8c808e1eb0>
query = 'SELECT "api_company"."id", "api_company"."name", "api_company"."status", "api_company"."last_update", "api_company"."application_link", "api_company"."notes" FROM "api_company" ORDER BY "api_company"."id" ASC'
params = ()

    def execute(self, query, params=None):
        if params is None:
            return Database.Cursor.execute(self, query)
        query = self.convert_query(query)
>       return Database.Cursor.execute(self, query, params)
E       sqlite3.OperationalError: no such table: api_company

../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/sqlite3/base.py:423: OperationalError

The above exception was the direct cause of the following exception:

request = <SubRequest '_django_db_marker' for <Function test_one_company_success>>

    @pytest.fixture(autouse=True)
    def _django_db_marker(request) -> None:
        """Implement the django_db marker, internal to pytest-django.
    
        This will dynamically request the ``db``, ``transactional_db`` or
        ``django_db_reset_sequences`` fixtures as required by the django_db marker.
        """
        marker = request.node.get_closest_marker("django_db")
        if marker:
            transaction, reset_sequences, databases = validate_django_db(marker)
    
            # TODO: Use pytest Store (item.store) once that's stable.
            request.node._pytest_django_databases = databases
    
            if reset_sequences:
                request.getfixturevalue("django_db_reset_sequences")
            elif transaction:
                request.getfixturevalue("transactional_db")
            else:
>               request.getfixturevalue("db")

../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/pytest_django/plugin.py:470: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/pytest_django/fixtures.py:120: in django_db_setup
    **setup_databases_args
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/test/utils.py:183: in setup_databases
    serialize=connection.settings_dict['TEST'].get('SERIALIZE', True),
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/base/creation.py:90: in create_test_db
    self.connection._test_serialized_contents = self.serialize_db_to_string()
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/base/creation.py:136: in serialize_db_to_string
    serializers.serialize("json", get_objects(), indent=None, stream=out)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/core/serializers/__init__.py:129: in serialize
    s.serialize(queryset, **options)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/core/serializers/base.py:90: in serialize
    for count, obj in enumerate(queryset, start=1):
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/base/creation.py:133: in get_objects
    yield from queryset.iterator()
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/models/query.py:353: in _iterator
    yield from self._iterable_class(self, chunked_fetch=use_chunked_fetch, chunk_size=chunk_size)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/models/query.py:51: in __iter__
    results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/models/sql/compiler.py:1175: in execute_sql
    cursor.execute(sql, params)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/utils.py:66: in execute
    return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/utils.py:75: in _execute_with_wrappers
    return executor(sql, params, many, context)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/utils.py:84: in _execute
    return self.cursor.execute(sql, params)
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/utils.py:90: in __exit__
    raise dj_exc_value.with_traceback(traceback) from exc_value
../../../.local/share/virtualenvs/django-restAPI-5iRsuPs6/lib/python3.7/site-packages/django/db/backends/utils.py:84: in _execute
    return self.cursor.execute(sql, params)

标签: pythondjangogithubpytestworkflow

解决方案


推荐阅读