Learning Platform
Глоссарий Troubleshooting
Урок 10.05 · 22 мин
Продвинутый
AdapterTestingPackagingTrusted Adapter Program

dbt-tests-adapter suite, packaging, Trusted Adapter Program

Заключительный урок модуля — comprehensive review of adapter testing, packaging, и пути к Trusted Adapter Program (TAP). Это production discipline для serious adapter projects.


pytest: test discovery и AAA pattern Fixtures и scope hierarchy в pytest

dbt-tests-adapter package

Что это: Pip package от dbt Labs с base test classes. Used для adapter conformance testing.

Install:

pip install dbt-tests-adapter

Source: https://github.com/dbt-labs/dbt-adapters/tree/main/dbt-tests-adapter

Содержит ~50 test classes covering:

Basic operations

  • BaseSimpleMaterializations — view, table, ephemeral
  • BaseEphemeral — ephemeral materialization edge cases
  • BaseValidateConnection — connection validation

Generic tests

  • BaseGenericTests — unique, not_null, accepted_values, relationships
  • BaseSingularTests — singular SQL tests

Materializations

  • BaseIncremental — basic incremental
  • BaseIncrementalUniqueKey — incremental с unique_key
  • BaseIncrementalOnSchemaChange — schema evolution
  • BaseIncrementalNotSchemaChange — vs schema change

Snapshots

  • BaseSnapshots — timestamp strategy
  • BaseSnapshotCheckCols — check strategy
  • BaseSnapshotTimestamps — timestamp edge cases
  • BaseSnapshotHardDeletes — hard_deletes config

Seeds

  • BaseSeeds — basic seeds
  • BaseSeedColumnQuoting — column name quoting
  • BaseSeedTimezone — timezone handling

Concurrency

  • BaseConcurrency — parallel runs

Schema management

  • BaseConstraintsRuntimeDdlEnforcement — constraints
  • BaseConstraintsRollback — rollback on constraint violation
  • BaseModelContractsConstraints — contracts

Docs

  • BaseDocsGenerate — docs generation
  • BaseDocsGenerateBase — base
  • BasePersistDocs — persist docs to warehouse

Exposures

  • BaseExposures — exposures работают

Sources

  • BaseSourceFreshness — source freshness

Grants

  • BaseGrants — basic grants
  • BaseIncrementalGrants — grants on incremental
  • BaseSnapshotGrants — grants on snapshots
  • BaseSeedGrants — grants on seeds

Operations

  • BaseRunOperation — run-operation command
  • BaseDbtClone — dbt clone command (1.7+)

How to integrate

В scaffold, tests/functional/adapter/test_basic.py:

from dbt.tests.adapter.basic.test_base import BaseSimpleMaterializations
from dbt.tests.adapter.basic.test_singular_tests import BaseSingularTests
from dbt.tests.adapter.basic.test_singular_tests_ephemeral import BaseSingularTestsEphemeral
from dbt.tests.adapter.basic.test_empty import BaseEmpty
from dbt.tests.adapter.basic.test_ephemeral import BaseEphemeral
from dbt.tests.adapter.basic.test_incremental import BaseIncremental
from dbt.tests.adapter.basic.test_generic_tests import BaseGenericTests
from dbt.tests.adapter.basic.test_snapshot_check_cols import BaseSnapshotCheckCols
from dbt.tests.adapter.basic.test_snapshot_timestamp import BaseSnapshotTimestamp
from dbt.tests.adapter.basic.test_adapter_methods import BaseAdapterMethod
from dbt.tests.adapter.basic.test_docs_generate import BaseDocsGenerate, BaseDocsGenerateReferences


class TestSimpleMaterializationsMyAdapter(BaseSimpleMaterializations):
    pass

class TestSingularTestsMyAdapter(BaseSingularTests):
    pass

class TestSingularTestsEphemeralMyAdapter(BaseSingularTestsEphemeral):
    pass

class TestEmptyMyAdapter(BaseEmpty):
    pass

class TestEphemeralMyAdapter(BaseEphemeral):
    pass

class TestIncrementalMyAdapter(BaseIncremental):
    pass

class TestGenericTestsMyAdapter(BaseGenericTests):
    pass

class TestSnapshotCheckColsMyAdapter(BaseSnapshotCheckCols):
    pass

class TestSnapshotTimestampMyAdapter(BaseSnapshotTimestamp):
    pass

class TestBaseAdapterMethodMyAdapter(BaseAdapterMethod):
    pass

class TestDocsGenerateMyAdapter(BaseDocsGenerate):
    pass

class TestDocsGenReferencesMyAdapter(BaseDocsGenerateReferences):
    pass

Каждый class — это inheritance. Tests run automatically.

Run:

pytest tests/functional/adapter/test_basic.py -v

Customization когда tests don’t fit

Sometimes base tests assume specific behavior. Override через fixtures:

class TestSimpleMaterializationsMyAdapter(BaseSimpleMaterializations):
    @pytest.fixture(scope='class')
    def project_config_update(self):
        return {
            'models': {
                '+materialized': 'view',
            }
        }
    
    @pytest.fixture(scope='class')
    def expected_relation_types(self):
        # Override expected relation types if your adapter differs
        return {
            'view_model': 'VIEW',
            'table_model': 'BASE TABLE',
        }

Other fixtures можно override:

  • project_files — model SQL files
  • seed_files — CSV files
  • models — model configs
  • snapshots — snapshot configs
  • tests — test SQL

Opt-out approach

If warehouse genuinely doesn’t support feature, skip test:

class TestSnapshotsMyAdapter(BaseSnapshotCheckCols):
    @pytest.mark.skip(reason='MyAdapter does not support check_cols strategy')
    def test_snapshot_check_cols(self, project):
        pass

Document opt-outs в README. Reasonable opt-outs accepted by TAP review.


Setup для адаптер тестов

Conftest.py (test setup):

# tests/conftest.py
import pytest

pytest_plugins = ['dbt.tests.fixtures.project']


def pytest_addoption(parser):
    parser.addoption('--profile', action='store', default='myadapter', type=str)


@pytest.fixture(scope='class')
def dbt_profile_target():
    return {
        'type': 'myadapter',
        'host': os.environ.get('DBT_TEST_HOST', 'localhost'),
        'user': os.environ.get('DBT_TEST_USER', 'root'),
        'password': os.environ.get('DBT_TEST_PASSWORD', ''),
        'database': os.environ.get('DBT_TEST_DATABASE', 'test'),
        'schema': os.environ.get('DBT_TEST_SCHEMA', 'public'),
    }

Environment variables:

# .env (not committed)
DBT_TEST_HOST=localhost
DBT_TEST_USER=test_user
DBT_TEST_PASSWORD=test_pass
DBT_TEST_DATABASE=test_db
DBT_TEST_SCHEMA=test_schema

Local run:

source .env
pytest tests/functional/adapter/ -v

CI integration

GitHub Actions (генерирован cookiecutter):

# .github/workflows/test.yml
name: Adapter Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      myadapter:
        image: myadapter/myadapter-ce:latest
        ports:
          - 5432:5432
        env:
          POSTGRES_PASSWORD: test
    
    strategy:
      matrix:
        python-version: ['3.9', '3.10', '3.11', '3.12']
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
      
      - name: Install
        run: |
          pip install -e .
          pip install -r dev-requirements.txt
      
      - name: Run tests
        run: pytest tests/functional/adapter/ -v
        env:
          DBT_TEST_HOST: localhost
          DBT_TEST_USER: test
          DBT_TEST_PASSWORD: test
          DBT_TEST_DATABASE: postgres

Packaging для distribution

setup.py (для PyPI release):

from setuptools import setup, find_namespace_packages

VERSION = '1.0.0'

setup(
    name='dbt-myadapter',
    version=VERSION,
    description='A dbt adapter for MyWarehouse',
    long_description=open('README.md').read(),
    long_description_content_type='text/markdown',
    author='Your Name',
    author_email='[email protected]',
    license='Apache-2.0',
    url='https://github.com/your-org/dbt-myadapter',
    
    packages=find_namespace_packages(include=['dbt.*']),
    package_data={
        'dbt': [
            'include/myadapter/dbt_project.yml',
            'include/myadapter/sample_profiles.yml',
            'include/myadapter/profile_template.yml',
            'include/myadapter/macros/*.sql',
            'include/myadapter/macros/**/*.sql',
        ],
    },
    
    install_requires=[
        'dbt-adapters>=1.7,<2.0',
        'dbt-common>=1.10,<2.0',
        'dbt-core>=1.8,<2.0',
        'pymysql>=1.0',
    ],
    
    python_requires='>=3.9',
    
    classifiers=[
        'Development Status :: 5 - Production/Stable',
        'License :: OSI Approved :: Apache Software License',
        'Operating System :: Microsoft :: Windows',
        'Operating System :: MacOS :: MacOS X',
        'Operating System :: POSIX :: Linux',
        'Programming Language :: Python :: 3.9',
        'Programming Language :: Python :: 3.10',
        'Programming Language :: Python :: 3.11',
        'Programming Language :: Python :: 3.12',
    ],
)

Build и upload:

python setup.py sdist bdist_wheel
twine upload dist/*

Now installable: pip install dbt-myadapter.


AdapterPlugin discovery

# dbt/adapters/myadapter/__init__.py
from dbt.adapters.myadapter.connections import MyConnectionManager, MyCredentials
from dbt.adapters.myadapter.impl import MyAdapter
from dbt.adapters.base import AdapterPlugin
from dbt.include import myadapter

Plugin = AdapterPlugin(
    adapter=MyAdapter,
    credentials=MyCredentials,
    include_path=myadapter.PACKAGE_PATH,
)

How dbt-core discovers:

  1. User installs pip install dbt-myadapter.
  2. Pip places package в site-packages: site-packages/dbt/adapters/myadapter/.
  3. dbt-core scans dbt.adapters.* namespace.
  4. Finds Plugin attribute в __init__.py.
  5. Registers in FACTORY.plugins['myadapter'] = Plugin.
  6. User’s profiles.yml type: myadapter -> finds Plugin -> instantiates adapter.

Без AdapterPlugin export — adapter не найден.


Trusted Adapter Program (TAP)

What it is: dbt Labs’ program признающий production-ready adapters.

Benefits:

  • Listed на docs.getdbt.com/docs/trusted-adapters
  • Discoverability через dbt Cloud Marketplace
  • Community trust
  • Endorsement by dbt Labs

Requirements:

  1. All dbt-tests-adapter pass (or documented opt-outs)
  2. Public GitHub repo с CI/CD
  3. PyPI package with regular releases
  4. Active maintainership:
    • Responsive к issues
    • Regular releases following dbt-core versions
    • Public roadmap
  5. Documentation:
    • README aligned с getdbt.com style
    • Migration guides
    • Examples
    • Troubleshooting
  6. Security review:
    • No hardcoded secrets
    • Sensitive data sanitization
    • Authentication best practices

Application process:

  1. Self-assessment — все criteria met?
  2. Submit application к dbt Labs (typically через GitHub issue или docs)
  3. Review — dbt Labs evaluates (1-2 months)
  4. Feedback — usually requested improvements
  5. Iterate — address feedback
  6. Approval — adapter listed
  7. Maintenance — continue maintenance per program

Timeline: 9-12 months from start to TAP listing.


Examples Trusted Adapters

Current Trusted Adapters (2026):

  • dbt-labs maintained (Tier 1):

    • dbt-postgres, dbt-redshift, dbt-snowflake, dbt-bigquery, dbt-spark, dbt-duckdb
  • Community Trusted (Tier 2):

    • dbt-databricks (Databricks)
    • dbt-trino (Trino / Starburst)
    • dbt-clickhouse (ClickHouse)
    • dbt-doris (Doris)
    • dbt-firebolt (Firebolt)
    • dbt-impala (Impala)
    • dbt-rockset (Rockset)
    • dbt-singlestore (SingleStore)
    • dbt-synapse (Azure Synapse)
    • dbt-vertica (Vertica)
    • dbt-greenplum (Greenplum)
    • dbt-iotdb (Apache IoTDB)
    • dbt-materialize (Materialize)
  • Community (not Trusted):

    • Various proprietary, newer adapters
    • Used at your own risk

Maintenance commitments

After TAP listing:

Release cadence:

  • Follow dbt-core releases (minor version)
  • Patch releases для bug fixes
  • Major versions для breaking changes (with deprecation period)

Issue management:

  • Acknowledge within 1 week
  • Triage и label
  • Critical security issues — fast turnaround

Community engagement:

  • Slack channel (dbt-community)
  • GitHub discussions
  • Annual contributor meetings

Documentation:

  • Keep up-to-date с dbt-core changes
  • Update for new warehouse features
  • Migration guides

This is long-term commitment. Adapters не one-shot projects.


Examples: dbt-snowflake structure

dbt-snowflake/
├── setup.py
├── pyproject.toml
├── README.md
├── LICENSE.md
├── CHANGELOG.md
├── .github/
│   └── workflows/
├── dbt/
│   ├── adapters/
│   │   └── snowflake/
│   │       ├── __init__.py
│   │       ├── __version__.py
│   │       ├── connections.py
│   │       ├── impl.py
│   │       ├── relation.py
│   │       ├── column.py
│   │       └── ...
│   └── include/
│       └── snowflake/
│           ├── dbt_project.yml
│           ├── profile_template.yml
│           ├── sample_profiles.yml
│           └── macros/
└── tests/
    ├── functional/
    ├── unit/
    └── integration/

Standard structure across all Trusted Adapters.


Попробуй сам

  1. Install dbt-tests-adapter:
pip install dbt-tests-adapter
  1. Look at source:
find $(pip show dbt-tests-adapter | grep Location | cut -d' ' -f2)/dbt/tests/adapter -name "*.py" | head -20
  1. Read one test class — test_base.py:
cat $(pip show dbt-tests-adapter | grep Location | cut -d' ' -f2)/dbt/tests/adapter/basic/test_base.py

Understand structure: setup fixtures, model/seed files, test methods.

  1. In your adapter scaffold, add test_basic.py с base classes.

  2. Run:

pytest tests/functional/adapter/ -v

Verbose output — see passing/failing tests.

  1. Iterate based on failures.

Ключевые выводы

  1. dbt-tests-adapter — official conformance test suite. ~50 base test classes.

  2. Integration через inheritance: class TestX(BaseY): pass. No test code from вас.

  3. Customization через fixture overrides когда warehouse differs.

  4. Opt-outs для truly unsupported features (с documentation).

  5. Packaging: setup.py с namespace package + package_data для macros.

  6. AdapterPlugin в __init__.py — discovery point.

  7. Trusted Adapter Program — pathway для production credibility. 9-12 months timeline. Requires full tests pass, maintenance commitment, documentation, security review.

  8. Trusted Adapters listed: dbt-postgres, dbt-snowflake, dbt-bigquery, dbt-duckdb, dbt-databricks, dbt-trino, etc.

  9. Maintenance ongoing: follow dbt-core releases, respond к issues, community engagement.

  10. Adapter development = months/years project. Plan accordingly.

Проверка знанийKnowledge check
Senior пишет adapter для proprietary warehouse внутри компании (internal only, never public). Нужно ли подать в Trusted Adapter Program?
ОтветAnswer
**Нет** — TAP только для public/community adapters. Но **best practices** все равно apply.\n\n**TAP context**:\n\nTrusted Adapter Program — designed для:\n- Public adapters (PyPI distributed)\n- Community endorsement\n- dbt Cloud integration\n- Public discoverability\n\nFor **internal adapter** не нужно applying — no public concerns.\n\n**Но best practices apply**:\n\n**1. dbt-tests-adapter conformance**:\n\nEven если internal, run conformance tests:\n\n```bash\npytest tests/functional/adapter/ -v\n```\n\nWhy:\n- Catches bugs пред internal users hit them\n- Documents capabilities\n- Sanity check после dependency updates\n- Onboarding new devs — tests document expected behavior\n\n**2. Documentation**:\n\nInternal README still important:\n- Setup instructions\n- Configuration options\n- Examples\n- Troubleshooting\n- Limitations\n\nNo one wants to debug undocumented adapter.\n\n**3. CI/CD**:\n\nInternal CI:\n\n```yaml\n# Internal GitLab/Jenkins\npipeline:\n - run: pytest tests/functional/adapter/\n - run: dbt build --target internal-ci\n```\n\nCatch regressions before propagating к users.\n\n**4. Versioning**:\n\nInternal версии помогают:\n- v0.1: PoC, expected to break\n- v1.0: First stable\n- v1.1: Bug fixes\n- v2.0: Breaking changes (с deprecation period)\n\nTeams могут pin к specific versions.\n\n**5. Maintenance plan**:\n\nEven internal:\n- Who maintains?\n- How updates pushed?\n- What happens когда original developer leaves?\n\nWithout plan — adapter becomes orphan, no one understands.\n\n**6. Security**:\n\nInternal usually not as exposed, но still:\n- No hardcoded credentials\n- Audit logging\n- Compliance с company policies\n\n**7. Performance**:\n\nWorkloads grow. Initial PoC adapter may fail на 1000+ models.\n\n- Benchmark раннее\n- Optimize hot paths\n- Profile slow operations\n\n**Tradeoffs for internal vs public**:\n\n| Aspect | Public (TAP) | Internal |\n|--------|-------------|----------|\n| Test coverage | 100% pass required | High desired |\n| Documentation | Comprehensive | Good enough для internal users |\n| Security review | External | Internal team |\n| Maintenance | Ongoing commitment | Ongoing for company need |\n| Release process | PyPI, formal | Git tag, internal package registry |\n| Community | Active engagement | Limited |\n| Roadmap | Public | Internal только |\n\n**Internal-only — quicker timeline**:\n\n- TAP: 9-12 months\n- Internal:\n - PoC: 1 week\n - Production ready: 3-6 months\n - Maintainable: ongoing\n\nNo external review process — faster iteration.\n\n**Decision framework**:\n\n**Do TAP if**:\n- Adapter will be public\n- Community endorsement valuable\n- Want dbt Cloud integration\n- Building business around adapter\n\n**Skip TAP if**:\n- Internal warehouse только\n- Proprietary tech (can't publish)\n- One company use case\n- Don't want maintenance burden\n\n**Hybrid approach**:\n\nStart internal. Use best practices. After 1+ years, if want to open-source — submit to TAP. Path's open.\n\n**Examples**:\n\n- **Internal-only adapter at FAANG company** — uses dbt-tests-adapter, comprehensive docs, but never public. Successful internally.\n\n- **Internal-first then public** — companies build internal adapter, eventually open source it. Adobe's Synapse adapter went this path.\n\n- **Public from start** — startups building data products often public from day 1 для discoverability.\n\n**Senior decision**: clearly communicate scope с stakeholders.\n\n'We're building internal adapter. No public release planned. Maintenance plan: <X> hours/month for 2 years.'\n\nClear expectations -> resourcing.\n\n**For learning purposes**: even building internal adapter, learn что TAP requires. It's **gold standard** для adapter development. Apply principles regardless of public release.\n\nЭто **professional discipline**. Internal adapters which follow TAP-like practices = better quality. Less debug. Happier users.
Проверка знанийKnowledge check
Senior хочет run dbt-tests-adapter suite, но 10 tests fail because warehouse doesn't support certain features (e.g., snapshots). Какие 3 правильных подхода?
ОтветAnswer
Тщательно ifferentiate **'doesn't support'** vs 'bug in adapter'. Both look like failures but require different fixes.\n\n**Approach 1 — Implement the feature (fix bug)**:\n\nIf failure из-за adapter bug:\n\n```\nFAILED test_simple_seed - AssertionError: column types don't match\n```\n\nThis is bug — type conversion wrong. Fix:\n\n```python\n@classmethod\ndef convert_text_type(cls, agate_table, col_idx):\n return 'STRING' # was: 'TEXT', should be 'STRING' for this warehouse\n```\n\nMost failures are bugs. Fix them.\n\n**Approach 2 — Skip с reason (warehouse limitation)**:\n\nIf warehouse genuinely doesn't support feature:\n\n```python\nclass TestSnapshotsMyAdapter(BaseSnapshotCheckCols):\n @pytest.mark.skip(reason='MyWarehouse does not support snapshot check_cols strategy')\n def test_snapshot_check(self, project):\n pass\n \n @pytest.mark.skip(reason='MyWarehouse does not support snapshot hard_deletes')\n def test_snapshot_hard_deletes(self, project):\n pass\n```\n\nGood reasons для skip:\n- Warehouse doesn't support feature (architecturally)\n- Warehouse-specific limitations (e.g., no MERGE на этом warehouse)\n- Inability that requires major rewrite (acceptable trade-off)\n\nDocument в README:\n\n```markdown\n## Unsupported Features\n\n### Snapshots\n- check_cols strategy: NOT supported (MyWarehouse has no row-level diff)\n- hard_deletes: NOT supported (no DELETE operation в core warehouse)\n\n### Materializations\n- materialized_view: NOT supported (MyWarehouse only имеет views, no MV concept)\n\nUse `table` or `view` materialization instead.\n```\n\nUsers know limitations upfront.\n\n**Approach 3 — Override с custom logic**:\n\nIf warehouse supports feature differently, override fixture:\n\n```python\nclass TestSimpleMaterializationsMyAdapter(BaseSimpleMaterializations):\n @pytest.fixture(scope='class')\n def expected_relation_types(self):\n # MyWarehouse calls views 'VIRTUAL_VIEW' not 'VIEW'\n return {\n 'view_model': 'VIRTUAL_VIEW',\n 'table_model': 'BASE_TABLE',\n 'swappable': 'VIRTUAL_VIEW',\n }\n \n @pytest.fixture(scope='class')\n def project_config_update(self):\n return {\n 'models': {\n '+materialized': 'view',\n }\n }\n```\n\nTest passes с your overrides.\n\n**Decision matrix**:\n\n| Scenario | Approach |\n|----------|----------|\n| Adapter bug | Approach 1 — Fix |\n| Warehouse fundamentally lacks feature | Approach 2 — Skip с reason |\n| Warehouse supports differently | Approach 3 — Override fixtures |\n| Edge case в test that doesn't apply | Approach 2 — Skip |\n| Performance optimization opportunity | Approach 1 — Improve |\n\n**Anti-patterns**:\n\n**Anti-pattern 1 — Skip without reason**:\n\n```python\[email protected]() # ← no reason\ndef test_x(self): pass\n```\n\nReader не знает why. Document.\n\n**Anti-pattern 2 — Skip потому что tests слишком complex**:\n\nFailure shows bug. Don't skip.\n\n**Anti-pattern 3 — Modify base test class**:\n\n```python\n# WRONG — monkey-patch base class\nBaseSnapshotCheckCols.test_snapshot_check = lambda self, project: None\n```\n\nBreaks other adapter implementations too. Just skip в your subclass.\n\n**Anti-pattern 4 — Override fixtures hide bugs**:\n\n```python\n# WRONG — override expected_relation_types к accept buggy output\[email protected]\ndef expected_relation_types(self):\n return {'view_model': 'VIEW'} # actual output from buggy adapter\n```\n\nMakes tests pass without fixing bug. Worse than failing test.\n\n**Right discipline**:\n\n1. **Run all tests**\n2. **For each failure** — diagnose:\n - Adapter bug? -> Approach 1 (fix)\n - Warehouse limitation? -> Approach 2 (skip)\n - Different behavior valid? -> Approach 3 (override)\n3. **Document everything** в README\n4. **CI verifies state** — no regressions\n\n**For TAP application**:\n\nReview process accepts:\n- All tests pass (ideal)\n- Some tests skipped с good reasons (acceptable)\n- Some tests с custom overrides documenting differences (acceptable)\n\nNot acceptable:\n- Tests skipped без reason\n- Tests pass via misleading overrides\n- Undocumented limitations\n\nReview judges **honesty и documentation**.\n\n**Production discipline**:\n\n1. **Maintain skip list** — track какие tests skipped\n2. **Periodically revisit** — может warehouse получил feature?\n3. **Update tests** когда warehouse evolves\n4. **CI gate** — only allow new skips с reason\n\n```yaml\n# CI script\n- run: pytest --markers | grep '@pytest.mark.skip' | wc -l\n# Track number of skips. Alert if grows.\n```\n\nЭто **honest professional development**. Show users true capabilities, не fake passing tests.

Проверьте понимание

Результат: 0 из 0
Прикладной
Вопрос 1 из 5. Что такое dbt-tests-adapter и зачем используется?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5