Problem
I’m trying to set up a test stage in Gitlab’s CI/CD. Locally, running the unit tests goes fine and as expected. In Gitlab’s CI/CD, though, when running the script coverage run manage.py test -v 2 && coverage report
the unit tests are executing before the migrations are completed in the test database, which is unexpected, and will always fail. Migrations on the test database need to run prior to unit tests executing.
Any idea why this behavior might happen?
When running python manage.py test
these are the steps that happen by default:
Creation of test databases.
Database migration.
Run system checks.
Running tests.
Report on the number of tests and success / failure.
Removing test databases.
In the local test run output below, you can see those exact steps, in that sequence happening. The problem is that in Gitlab’s CI/CD pipeline, step 2 and 4 are switched for some mysterious reason to me.
Things I’ve already tried
- Removing coverage, and only executing
python manage.py test
Result: same error - Removing user test, where Gitlab says the test fails: Result: same error
- Removing all but one unit test to attempt to isolate: Result: same error
- Adding and activating a virtual environment inside Gitlab’s container: Result: same error
- Trying a SQLite3: Result: migrations fail due to necessary array field necessary in the project not supported in SQLite3
Gitlab CI/CD Output
My actual output is much bigger. But here’s the relevant parts:
$ echo $TEST_SECRETS | base64 -d > config/settings/local.py $ echo RUNNING UNIT TESTS RUNNING UNIT TESTS $ coverage run manage.py test -v 2 && coverage report Creating test database for alias 'default' ('test_mia')... test_dataconnector_category_creation_delete (apps.core.tests.test_models.DataConnectorCategoryTestCase) Verify data connector category creation ... ok test_compare_airbytesetting (apps.services.tests.test_model_airbytesetting.AirbyteSettingModel_TestClass) Compare object from self.airbytesetting to ensure data is in proper format. ... ok test_fieldconstraints_airbytesetting (apps.services.tests.test_model_airbytesetting.AirbyteSettingModel_TestClass) Compares Field Constraint Values for Accuracy ... ok test_update_airbytesetting (apps.services.tests.test_model_airbytesetting.AirbyteSettingModel_TestClass) Test updating values in self.airbytesetting and saving that to the test database. ... ok test_compare_airbytesource (apps.services.tests.test_model_airbytesource.AirbyteSourceModel_TestClass) Compare object from self.airbytesource to ensure data is in proper format. ... ok test_delete_airbytesource (apps.services.tests.test_model_airbytesource.AirbyteSourceModel_TestClass) Test Deleting self.airbytesource object from database ... ok test_fieldconstraints_airbytesource (apps.services.tests.test_model_airbytesource.AirbyteSourceModel_TestClass) Compares Field Constraint Values for Accuracy ... ok test_update_airbytesource (apps.services.tests.test_model_airbytesource.AirbyteSourceModel_TestClass) Test updating values in self.airbytesource and saving that to the test database. ... ok test_compare_airbytestream (apps.services.tests.test_model_airbytestream.AirbyteStreamModel_TestClass) Compare object from self.airbytestream to ensure data is in proper format. ... ok test_delete_airbytestream (apps.services.tests.test_model_airbytestream.AirbyteStreamModel_TestClass) Test Deleting self.airbytestream object from database ... ok test_fieldconstraints_airbytestream (apps.services.tests.test_model_airbytestream.AirbyteStreamModel_TestClass) Compares Field Constraint Values for Accuracy ... ok test_update_airbytestream (apps.services.tests.test_model_airbytestream.AirbyteStreamModel_TestClass) Test updating values in self.airbytestream and saving that to the test database. ... ok test_compare_airbytesynccatalog (apps.services.tests.test_model_airbytesynccatalog.AirbyteSyncCatalogModel_TestClass) Compare object from self.airbytesynccatalog to ensure data is in proper format. ... ok test_delete_airbytesynccatalog (apps.services.tests.test_model_airbytesynccatalog.AirbyteSyncCatalogModel_TestClass) Test Deleting self.airbytesynccatalog object from database ... ok test_fieldconstraints_airbytesynccatalog (apps.services.tests.test_model_airbytesynccatalog.AirbyteSyncCatalogModel_TestClass) Compares Field Constraint Values for Accuracy ... ok test_update_airbytesynccatalog (apps.services.tests.test_model_airbytesynccatalog.AirbyteSyncCatalogModel_TestClass) Test updating values in self.airbytesynccatalog and saving that to the test database. ... ok test_compare_tableausetting (apps.services.tests.test_model_tableausetting.TableauSettingModel_TestClass) Compare object from self.tableausetting to ensure data is in proper format. ... ok test_createrandom_tableausetting (apps.services.tests.test_model_tableausetting.TableauSettingModel_TestClass) Test Creating a New (Second) TableauSetting Model Object with Random Values. ... ok test_delete_tableausetting (apps.services.tests.test_model_tableausetting.TableauSettingModel_TestClass) Test Deleting self.tableausetting object from database ... ok test_fieldconstraints_tableausetting (apps.services.tests.test_model_tableausetting.TableauSettingModel_TestClass) Compares Field Constraint Values for Accuracy ... ok test_update_tableausetting (apps.services.tests.test_model_tableausetting.TableauSettingModel_TestClass) Test updating values in self.tableausetting and saving that to the test database. ... ok test_get_namespace_custom_format (apps.services.tests.test_scripts_airbyte.ServicesTestCase) TO-DO: this method should live in AirByte class ... ok test_retrieve_me_user (apps.user.tests.test_views.UserViewTest) ... ok test_retrieve_user (apps.user.tests.test_views.UserViewTest) ... ok apps.user.tests.test_models (unittest.loader._FailedTest) ... ERROR ====================================================================== ERROR: apps.user.tests.test_models (unittest.loader._FailedTest) ---------------------------------------------------------------------- ImportError: Failed to import test module: apps.user.tests.test_models Traceback (most recent call last): File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) psycopg2.errors.UndefinedTable: relation "user_user" does not exist LINE 1: INSERT INTO "user_user" ("password", "last_login", "is_super... ^ The above exception was the direct cause of the following exception: Traceback (most recent call last): File "/usr/local/lib/python3.8/unittest/loader.py", line 436, in _find_test_path module = self._get_module_from_name(name) File "/usr/local/lib/python3.8/unittest/loader.py", line 377, in _get_module_from_name __import__(name) File "/builds/americommerce/mia/mia-ms-main/apps/user/tests/test_models.py", line 3, in <module> from apps.user.tests.factories import UserFactory, CompanyFactory, SignUpTokenFactory File "/builds/americommerce/mia/mia-ms-main/apps/user/tests/factories.py", line 23, in <module> class SignUpTokenFactory(factory.django.DjangoModelFactory): File "/builds/americommerce/mia/mia-ms-main/apps/user/tests/factories.py", line 28, in SignUpTokenFactory user = UserFactory() File "/usr/local/lib/python3.8/site-packages/factory/base.py", line 40, in __call__ return cls.create(**kwargs) File "/usr/local/lib/python3.8/site-packages/factory/base.py", line 528, in create return cls._generate(enums.CREATE_STRATEGY, kwargs) File "/usr/local/lib/python3.8/site-packages/factory/django.py", line 117, in _generate return super()._generate(strategy, params) File "/usr/local/lib/python3.8/site-packages/factory/base.py", line 465, in _generate return step.build() File "/usr/local/lib/python3.8/site-packages/factory/builder.py", line 262, in build instance = self.factory_meta.instantiate( File "/usr/local/lib/python3.8/site-packages/factory/base.py", line 317, in instantiate return self.factory._create(model, *args, **kwargs) File "/usr/local/lib/python3.8/site-packages/factory/django.py", line 166, in _create return manager.create(*args, **kwargs) File "/usr/local/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 447, in create obj.save(force_insert=True, using=self.db) File "/usr/local/lib/python3.8/site-packages/django/contrib/auth/base_user.py", line 67, in save super().save(*args, **kwargs) File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 753, in save self.save_base(using=using, force_insert=force_insert, File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 790, in save_base updated = self._save_table( File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 895, in _save_table results = self._do_insert(cls._base_manager, using, fields, returning_fields, raw) File "/usr/local/lib/python3.8/site-packages/django/db/models/base.py", line 933, in _do_insert return manager._insert( File "/usr/local/lib/python3.8/site-packages/django/db/models/manager.py", line 85, in manager_method return getattr(self.get_queryset(), name)(*args, **kwargs) File "/usr/local/lib/python3.8/site-packages/django/db/models/query.py", line 1254, in _insert return query.get_compiler(using=using).execute_sql(returning_fields) File "/usr/local/lib/python3.8/site-packages/django/db/models/sql/compiler.py", line 1397, in execute_sql cursor.execute(sql, params) File "/usr/local/lib/python3.8/site-packages/sentry_sdk/integrations/django/__init__.py", line 508, in execute return real_execute(self, sql, params) File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 66, in execute return self._execute_with_wrappers(sql, params, many=False, executor=self._execute) File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 75, in _execute_with_wrappers return executor(sql, params, many, context) File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) File "/usr/local/lib/python3.8/site-packages/django/db/utils.py", line 90, in __exit__ raise dj_exc_value.with_traceback(traceback) from exc_value File "/usr/local/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute return self.cursor.execute(sql, params) django.db.utils.ProgrammingError: relation "user_user" does not exist LINE 1: INSERT INTO "user_user" ("password", "last_login", "is_super... ^ ---------------------------------------------------------------------- Ran 25 tests in 0.648s FAILED (errors=1) Destroying test database for alias 'default' ('test_mia')... Operations to perform: Synchronize unmigrated apps: corsheaders, debug_toolbar, delete_migrations, django_filters, generic_relations, messages, mptt, rest_framework, rest_framework_jwt, rest_framework_swagger, staticfiles Apply all migrations: admin, auth, contenttypes, core, dashboard, reports, services, sessions, store, user Synchronizing apps without migrations: Creating tables... Running deferred SQL... Running migrations: Applying contenttypes.0001_initial... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0001_initial... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying user.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying store.0001_initial... OK Applying dashboard.0001_initial... OK Applying core.0001_initial... OK Applying reports.0001_initial... OK Applying reports.0002_emailreport_store... OK Applying reports.0003_initial... OK Applying reports.0004_remove_emailreport_segments... OK Applying core.0002_dataconnectorcategory... OK Applying core.0003_auto_20220331_1610... OK Applying core.0004_delete_segment... OK Applying dashboard.0002_auto_20220331_1804... OK Applying dashboard.0003_flag_tableauview_widget_widgetsection... OK Applying reports.0005_emailreport_segments... OK Applying services.0001_initial... OK Applying services.0002_auto_20220325_0426... OK Applying services.0003_auto_20220331_1610... OK Applying sessions.0001_initial... OK Applying store.0002_initial... OK Applying store.0003_storefrontdataconnector... OK Applying user.0002_auto_20220325_0426... OK Applying user.0003_auto_20220331_1610... OK Applying user.0004_auto_20220331_1804... OK Applying user.0005_user_flags... OK System check identified no issues (0 silenced). Cleaning up project directory and file based variables 00:01 ERROR: Job failed: exit code 1
Local Test Run Output
Creating test database for alias 'default' ('test_mia')... Operations to perform: Synchronize unmigrated apps: corsheaders, debug_toolbar, delete_migrations, django_filters, generic_relations, messages, mptt, rest_framework, rest_framework_jwt, rest_framework_swagger, staticfiles Apply all migrations: admin, auth, contenttypes, core, dashboard, reports, services, sessions, store, user Synchronizing apps without migrations: Creating tables... Running deferred SQL... Running migrations: Applying contenttypes.0001_initial... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0001_initial... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying auth.0012_alter_user_first_name_max_length... OK Applying user.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying store.0001_initial... OK Applying dashboard.0001_initial... OK Applying core.0001_initial... OK Applying reports.0001_initial... OK Applying reports.0002_emailreport_store... OK Applying reports.0003_initial... OK Applying reports.0004_remove_emailreport_segments... OK Applying core.0002_dataconnectorcategory... OK Applying core.0003_auto_20220331_1610... OK Applying core.0004_delete_segment... OK Applying dashboard.0002_auto_20220331_1804... OK Applying dashboard.0003_flag_tableauview_widget_widgetsection... OK Applying reports.0005_emailreport_segments... OK Applying services.0001_initial... OK Applying services.0002_auto_20220325_0426... OK Applying services.0003_auto_20220331_1610... OK Applying sessions.0001_initial... OK Applying store.0002_initial... OK Applying store.0003_storefrontdataconnector... OK Applying user.0002_auto_20220325_0426... OK Applying user.0003_auto_20220331_1610... OK Applying user.0004_auto_20220331_1804... OK Applying user.0005_user_flags... OK System check identified no issues (0 silenced). test_dataconnector_category_creation_delete (apps.core.tests.test_models.DataConnectorCategoryTestCase) Verify data connector category creation ... ok test_compare_airbytesetting (apps.services.tests.test_model_airbytesetting.AirbyteSettingModel_TestClass) Compare object from self.airbytesetting to ensure data is in proper format. ... ok test_fieldconstraints_airbytesetting (apps.services.tests.test_model_airbytesetting.AirbyteSettingModel_TestClass) Compares Field Constraint Values for Accuracy ... ok test_update_airbytesetting (apps.services.tests.test_model_airbytesetting.AirbyteSettingModel_TestClass) Test updating values in self.airbytesetting and saving that to the test database. ... ok test_compare_airbytesource (apps.services.tests.test_model_airbytesource.AirbyteSourceModel_TestClass) Compare object from self.airbytesource to ensure data is in proper format. ... ok test_delete_airbytesource (apps.services.tests.test_model_airbytesource.AirbyteSourceModel_TestClass) Test Deleting self.airbytesource object from database ... ok test_fieldconstraints_airbytesource (apps.services.tests.test_model_airbytesource.AirbyteSourceModel_TestClass) Compares Field Constraint Values for Accuracy ... ok test_update_airbytesource (apps.services.tests.test_model_airbytesource.AirbyteSourceModel_TestClass) Test updating values in self.airbytesource and saving that to the test database. ... ok test_compare_airbytestream (apps.services.tests.test_model_airbytestream.AirbyteStreamModel_TestClass) Compare object from self.airbytestream to ensure data is in proper format. ... ok test_delete_airbytestream (apps.services.tests.test_model_airbytestream.AirbyteStreamModel_TestClass) Test Deleting self.airbytestream object from database ... ok test_fieldconstraints_airbytestream (apps.services.tests.test_model_airbytestream.AirbyteStreamModel_TestClass) Compares Field Constraint Values for Accuracy ... ok test_update_airbytestream (apps.services.tests.test_model_airbytestream.AirbyteStreamModel_TestClass) Test updating values in self.airbytestream and saving that to the test database. ... ok test_compare_airbytesynccatalog (apps.services.tests.test_model_airbytesynccatalog.AirbyteSyncCatalogModel_TestClass) Compare object from self.airbytesynccatalog to ensure data is in proper format. ... ok test_delete_airbytesynccatalog (apps.services.tests.test_model_airbytesynccatalog.AirbyteSyncCatalogModel_TestClass) Test Deleting self.airbytesynccatalog object from database ... ok test_fieldconstraints_airbytesynccatalog (apps.services.tests.test_model_airbytesynccatalog.AirbyteSyncCatalogModel_TestClass) Compares Field Constraint Values for Accuracy ... ok test_update_airbytesynccatalog (apps.services.tests.test_model_airbytesynccatalog.AirbyteSyncCatalogModel_TestClass) Test updating values in self.airbytesynccatalog and saving that to the test database. ... ok test_compare_tableausetting (apps.services.tests.test_model_tableausetting.TableauSettingModel_TestClass) Compare object from self.tableausetting to ensure data is in proper format. ... ok test_createrandom_tableausetting (apps.services.tests.test_model_tableausetting.TableauSettingModel_TestClass) Test Creating a New (Second) TableauSetting Model Object with Random Values. ... ok test_delete_tableausetting (apps.services.tests.test_model_tableausetting.TableauSettingModel_TestClass) Test Deleting self.tableausetting object from database ... ok test_fieldconstraints_tableausetting (apps.services.tests.test_model_tableausetting.TableauSettingModel_TestClass) Compares Field Constraint Values for Accuracy ... ok test_update_tableausetting (apps.services.tests.test_model_tableausetting.TableauSettingModel_TestClass) Test updating values in self.tableausetting and saving that to the test database. ... ok test_get_namespace_custom_format (apps.services.tests.test_scripts_airbyte.ServicesTestCase) TO-DO: this method should live in AirByte class ... ok test_company_creation (apps.user.tests.test_models.UserTestCase) Verify Company creation ... ok test_signup_token_creation (apps.user.tests.test_models.UserTestCase) Verify token creation ... ok test_user_creation (apps.user.tests.test_models.UserTestCase) Verify User creation ... ok test_user_creation_with_company (apps.user.tests.test_models.UserTestCase) Verify User can be assigned company ... ok test_user_creation_with_signup_token (apps.user.tests.test_models.UserTestCase) Verify User can be assigned token ... ok test_retrieve_me_user (apps.user.tests.test_views.UserViewTest) ... ok test_retrieve_user (apps.user.tests.test_views.UserViewTest) ... ok ---------------------------------------------------------------------- Ran 29 tests in 0.521s OK Destroying test database for alias 'default' ('test_mia')... Name Stmts Miss Cover --------------------------------------------------------------------- apps/core/admin.py 5 0 100% apps/core/exceptions.py 22 3 86% apps/core/mixins.py 10 3 70% apps/core/models.py 8 1 88% apps/core/serializers.py 18 0 100% apps/core/urls.py 5 0 100% apps/core/utils.py 69 33 52% apps/core/views.py 42 26 38% apps/dashboard/admin.py 15 0 100% apps/dashboard/models.py 34 7 79% apps/dashboard/urls.py 6 0 100% apps/dashboard/views.py 27 16 41% apps/delete_migrations/admin.py 1 0 100% apps/delete_migrations/models.py 1 0 100% apps/reports/admin.py 14 0 100% apps/reports/mixins.py 16 9 44% apps/reports/models/alert.py 47 3 94% apps/reports/models/email_report.py 31 1 97% apps/reports/serializers/alert.py 49 14 71% apps/reports/serializers/email_report.py 49 22 55% apps/reports/tasks.py 71 43 39% apps/reports/urls.py 9 0 100% apps/reports/utils.py 12 9 25% apps/reports/views/alerts.py 40 23 42% apps/reports/views/reports.py 55 26 53% apps/services/admin.py 23 6 74% apps/services/models/airbyte.py 37 0 100% apps/services/models/tableau.py 9 0 100% apps/services/scripts/airbyte.py 157 126 20% apps/services/scripts/tableau.py 69 50 28% apps/services/urls.py 3 0 100% apps/services/views.py 15 8 47% apps/store/admin.py 41 0 100% apps/store/mixins.py 12 7 42% apps/store/models/amazon_ads.py 18 2 89% apps/store/models/amazon_seller_partner.py 21 2 90% apps/store/models/data_connector_base.py 28 5 82% apps/store/models/data_connector_settings.py 18 3 83% apps/store/models/facebook.py 17 2 88% apps/store/models/google_ads.py 20 2 90% apps/store/models/google_analytics.py 18 2 89% apps/store/models/google_search_console.py 18 2 89% apps/store/models/hubspot.py 14 2 86% apps/store/models/klaviyo.py 12 1 92% apps/store/models/mailchimp.py 14 2 86% apps/store/models/shopify.py 17 2 88% apps/store/models/store.py 13 3 77% apps/store/models/storefront.py 19 8 58% apps/store/serializers/amazon_ads.py 22 11 50% apps/store/serializers/amazon_seller_partner.py 26 14 46% apps/store/serializers/base.py 8 0 100% apps/store/serializers/credentials.py 3 0 100% apps/store/serializers/facebook.py 15 7 53% apps/store/serializers/google_ads.py 23 12 48% apps/store/serializers/google_analytics.py 22 11 50% apps/store/serializers/google_search_console.py 22 11 50% apps/store/serializers/hubspot.py 6 0 100% apps/store/serializers/klaviyo.py 6 0 100% apps/store/serializers/list_data_connectors.py 24 0 100% apps/store/serializers/mailchimp.py 6 0 100% apps/store/serializers/shopify.py 21 10 52% apps/store/serializers/store.py 30 3 90% apps/store/serializers/storefront.py 6 0 100% apps/store/tasks.py 104 80 23% apps/store/urls.py 26 0 100% apps/store/utils.py 89 66 26% apps/store/views/check_data_connector_store.py 21 13 38% apps/store/views/credentials.py 13 3 77% apps/store/views/delete_data_connector.py 19 10 47% apps/store/views/detail_data_connectors.py 42 0 100% apps/store/views/list_data_connectors.py 50 32 36% apps/store/views/reset_data_connector.py 18 9 50% apps/store/views/store.py 15 8 47% apps/store/views/sync_data_connector.py 18 9 50% apps/user/admin.py 29 7 76% apps/user/apps.py 5 0 100% apps/user/forms.py 14 1 93% apps/user/models.py 72 13 82% apps/user/serializers.py 88 31 65% apps/user/signals.py 11 4 64% apps/user/tasks.py 15 6 60% apps/user/urls.py 7 0 100% apps/user/utils.py 4 0 100% apps/user/validators.py 7 2 71% apps/user/views.py 61 26 57% manage.py 13 6 54% --------------------------------------------------------------------- TOTAL 2250 879 61%
.gitlab-ci.yml file
My actual file is much bigger. But here’s the relevant parts:
services: - docker:20.10.7-dind - postgres:latest stages: - test variables: POSTGRES_DB: test_mia POSTGRES_USER: mia_dev POSTGRES_PASSWORD: test123 build_test_db: image: postgres script: - export PGPASSWORD=$POSTGRES_PASSWORD - psql -h "postgres" -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "SELECT 'OK' AS status;" test: image: python:3.8-slim-buster stage: test before_script: - | apt-get update && apt-get install -y --no-install-recommends gcc musl-dev python3-dev libpq-dev libgnutls28-dev git && rm -rf /var/lib/apt/lists/* - python -m pip install --upgrade pip - pip install uwsgi - pip install -r requirements.txt - echo $TEST_SECRETS | base64 -d > config/settings/local.py script: - echo RUNNING UNIT TESTS - coverage run manage.py test -v 2 && coverage report needs: - ["build_test_db"] environment: test # only: # - develop
Advertisement
Answer
Add a python manage.py migrate
line before coverage run manage.py test ...
in the scripts section of your gitlab-ci.yml
.