dist/cursor/saleor-commerce/skills/saleor-testing/SKILL.md
Test Saleor applications — pytest setup, Django test client, GraphQL test patterns, App testing, factory_boy fixtures, and webhook testing. Use when writing tests for Saleor projects.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins saleor-testingInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
site:github.com/saleor/saleor pytest conftest fixtures for Saleor's test setup and existing fixturessite:docs.saleor.io app testing webhooks for App testing patterns and webhook verificationsite:docs.saleor.io graphql API testing for GraphQL query and mutation testing approacheshttps://github.com/saleor/saleor/blob/main/conftest.py and review root-level test configurationsite:docs.pytest.org fixtures factory_boy django for pytest fixtures and factory_boy integrationSaleor follows a layered testing approach:
| Layer | Tool | Purpose | |-------|------|---------| | Unit tests | pytest | Test individual functions, utilities, and model methods | | Integration tests | Django test client | Test GraphQL API endpoints with database | | App tests | pytest + httpx/requests-mock | Test App webhooks, signature verification | | E2E tests | pytest + API client | Test full user flows through the API |
| File | Purpose |
|------|---------|
| pytest.ini / pyproject.toml | Pytest settings, markers, default flags |
| conftest.py (root) | Shared fixtures, database setup, API clients |
| conftest.py (per-app) | App-specific fixtures and helpers |
| Setting | Value | Purpose |
|---------|-------|---------|
| DJANGO_SETTINGS_MODULE | saleor.tests.settings | Test-specific Django settings |
| --reuse-db | Flag | Reuse test database across runs for speed |
| --no-migrations | Flag | Skip migrations; create tables directly |
| -x | Flag | Stop on first failure during development |
| -n auto | Flag | Parallel execution with pytest-xdist |
| Plugin | Purpose |
|--------|---------|
| pytest-django | Django integration, database access, settings override |
| pytest-xdist | Parallel test execution |
| pytest-mock | Mock and patch utilities |
| pytest-asyncio | Async test support |
| pytest-vcr | Record and replay HTTP interactions |
| pytest-factoryboy | factory_boy integration with pytest fixtures |
Saleor's GraphQL API is tested using Django's test client:
| Component | Description |
|-----------|-------------|
| Test client | Django Client or RequestFactory for HTTP requests |
| Endpoint | POST to /graphql/ with query and variables |
| Authentication | Set HTTP_AUTHORIZATION header with JWT or App token |
| Content type | application/json for standard queries |
| Actor | Header | Token Source |
|-------|--------|--------------|
| Anonymous | None | No authentication |
| Customer | Authorization: Bearer <jwt> | tokenCreate or test fixture |
| Staff user | Authorization: Bearer <jwt> | Staff user fixture with permissions |
| App | Authorization: Bearer <app-token> | App token fixture |
| Assertion | What to Check |
|-----------|---------------|
| Status code | assert response.status_code == 200 |
| No errors | assert "errors" not in content or content["data"]["mutation"]["errors"] == [] |
| Data present | assert content["data"]["query"]["field"] == expected |
| Permission denied | assert content["errors"][0]["extensions"]["exception"]["code"] == "PermissionDenied" |
| Validation error | Check errors array in mutation response for field-level errors |
| Factory | Model | Key Fields |
|---------|-------|------------|
| UserFactory | User | email, first_name, last_name, is_staff |
| ProductTypeFactory | ProductType | name, has_variants, is_shipping_required |
| ProductFactory | Product | name, product_type, category, slug |
| ProductVariantFactory | ProductVariant | product, sku, track_inventory |
| CategoryFactory | Category | name, slug, parent |
| CollectionFactory | Collection | name, slug |
| ChannelFactory | Channel | name, slug, currency_code |
| WarehouseFactory | Warehouse | name, slug, address |
| OrderFactory | Order | user, channel, billing_address |
| OrderLineFactory | OrderLine | order, variant, quantity |
| CheckoutFactory | Checkout | channel, email, shipping_address |
| VoucherFactory | Voucher | code, type, discount_value |
| ShippingZoneFactory | ShippingZone | name, countries |
| ShippingMethodFactory | ShippingMethod | name, type, shipping_zone |
| Test Aspect | What to Verify | |-------------|----------------| | Payload structure | JSON schema matches expected format | | Required fields | All mandatory fields are present | | Data accuracy | Payload values match the triggering event | | Serialization | Dates, decimals, and enums serialize correctly |
| Step | Description |
|------|-------------|
| 1. Get key material | For JWS (default): use test JWKS; for legacy HMAC: use App secret key |
| 2. Sign payload | Create valid JWS/HMAC signature for the test payload |
| 3. Set header | Include signature in the Saleor-Signature header |
| 4. Verify in App | App verifies signature and processes payload |
| 5. Test mismatch | Verify App rejects requests with invalid signatures |
| Step | Description | |------|-------------| | 1. Create test data | Use factories to set up products, channels, etc. | | 2. Execute query | Send GraphQL query via test client | | 3. Assert results | Verify returned data matches created test data | | 4. Test filtering | Verify filters, search, and pagination work | | 5. Test permissions | Verify unauthorized users cannot access data |
| Step | Description | |------|-------------| | 1. Set up prerequisites | Create required related objects | | 2. Execute mutation | Send mutation with valid input | | 3. Assert success | Check response for data and no errors | | 4. Verify database | Query the database to confirm changes persisted | | 5. Test validation | Send invalid input and verify error messages | | 6. Test permissions | Verify only authorized users can execute |
| Fixture | Provides |
|---------|----------|
| staff_user | Authenticated staff user with configurable permissions |
| customer_user | Authenticated customer with address |
| channel_USD | Default USD channel |
| product | Product with type, category, variant, and channel listing |
| order | Order with lines, addresses, and payment |
| checkout | Checkout with lines and shipping address |
| warehouse | Warehouse with stock for test variants |
| shipping_zone | Shipping zone with methods and channel listing |
| Stage | Tests | Configuration |
|-------|-------|---------------|
| Pre-commit | Linting, type checks | pre-commit hooks with ruff, mypy |
| Unit tests | Fast, isolated tests | pytest -x --no-migrations -q |
| Integration tests | API and database tests | pytest --reuse-db -n auto |
| Coverage | Code coverage report | pytest --cov=saleor --cov-report=xml |
| App tests | Webhook and App tests | pytest tests/apps/ -v |
@pytest.mark.django_db on all tests that access the databasepytest-xdist for faster CI pipelinesassertNumQueries to catch N+1 query problems in resolversFetch the Saleor testing and pytest documentation for exact fixture patterns, test client setup, and CI configuration before implementing.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.