{"id":154,"date":"2026-10-12T14:41:00","date_gmt":"2026-10-12T14:41:00","guid":{"rendered":"http:\/\/www.scmgalaxy.com\/tutorials\/2010\/02\/09\/test-article\/"},"modified":"2026-10-12T14:41:00","modified_gmt":"2026-10-12T14:41:00","slug":"test-article","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/test-article\/","title":{"rendered":"CI\/CD Testing Strategy for DevOps Teams: From Commit to Production"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>Originally published on 2010-02-09; fully revised and updated.<\/em><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Effective DevOps delivery depends on fast, reliable testing from the first commit to production release. This guide helps platform engineers, SREs, and application teams design a practical CI\/CD testing strategy, map each test type to the right stage, and implement a maintainable pipeline.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What to test in a modern delivery pipeline<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A balanced pipeline uses multiple test layers instead of relying on a single end-to-end suite. Each layer answers a different risk question.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Lint and static checks:<\/strong> Catch syntax, style, and obvious quality issues quickly.<\/li><li><strong>Unit tests:<\/strong> Validate small code paths with fast feedback per commit.<\/li><li><strong>Integration tests:<\/strong> Verify service-to-service behavior and data interactions.<\/li><li><strong>End-to-end tests:<\/strong> Validate critical user journeys in a production-like flow.<\/li><li><strong>Smoke tests:<\/strong> Confirm core availability immediately after deployment.<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Commit-to-production testing workflow<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This workflow keeps early stages fast, then progressively increases confidence before promotion.<\/p>\n\n\n\n<pre class=\"mermaid\">\nflowchart TD\n    A[Developer pushes commit] --> B[CI pipeline starts]\n    B --> C[Lint and unit tests]\n    C --> D{Checks pass}\n    D -- no --> E[Fail fast and notify]\n    D -- yes --> F[Build artifact]\n    F --> G[Integration tests]\n    G --> H[Security and policy checks]\n    H --> I{Release gate approved}\n    I -- no --> J[Stop and investigate]\n    I -- yes --> K[Deploy to staging]\n    K --> L[Smoke tests then promote]\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Where each test type fits<\/h2>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Test type<\/th><th>Best pipeline stage<\/th><th>Primary goal<\/th><th>Execution profile<\/th><\/tr><\/thead><tbody><tr><td>Lint and static analysis<\/td><td>First CI stage<\/td><td>Fast quality guardrails<\/td><td>Very fast, runs on every change<\/td><\/tr><tr><td>Unit tests<\/td><td>Early CI stage<\/td><td>Logic correctness<\/td><td>Fast, high signal for regressions<\/td><\/tr><tr><td>Integration tests<\/td><td>Post-build CI stage<\/td><td>Dependency and API behavior<\/td><td>Medium runtime, environment-dependent<\/td><\/tr><tr><td>End-to-end tests<\/td><td>Staging or pre-release stage<\/td><td>Critical workflow validation<\/td><td>Slower, narrower scope recommended<\/td><\/tr><tr><td>Smoke tests<\/td><td>Immediately after deploy<\/td><td>Basic release health check<\/td><td>Fast, production-safety oriented<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Example CI pipeline configuration<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The following example shows a simple staged approach in GitHub Actions: lint and unit tests first, integration checks next, then a deployment gate.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php language-yaml\">name: ci\n\non:\n  push:\n    branches: &#91; main ]\n  pull_request:\n\njobs:\n  lint_unit:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n      - uses: actions\/setup-python@v5\n        with:\n          python-version: <span class=\"hljs-string\">\"3.11\"<\/span>\n      - run: pip install -r requirements.txt\n      - run: flake8 .\n      - run: pytest -q\n\n  integration:\n    runs-on: ubuntu-latest\n    needs: lint_unit\n    steps:\n      - uses: actions\/checkout@v4\n      - run: docker compose up -d\n      - run: pytest -m integration\n      - run: docker compose down -v\n\n  release_gate:\n    runs-on: ubuntu-latest\n    needs: integration\n    <span class=\"hljs-keyword\">if<\/span>: github.ref == <span class=\"hljs-string\">'refs\/heads\/main'<\/span>\n    steps:\n      - run: <span class=\"hljs-keyword\">echo<\/span> <span class=\"hljs-string\">\"Ready for staging deployment\"<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Example local commands for CI parity<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Developers should be able to run the same core checks locally to reduce CI failures and shorten feedback loops.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css language-bash\"><span class=\"hljs-selector-tag\">pip<\/span> <span class=\"hljs-selector-tag\">install<\/span> <span class=\"hljs-selector-tag\">-r<\/span> <span class=\"hljs-selector-tag\">requirements<\/span><span class=\"hljs-selector-class\">.txt<\/span>\n<span class=\"hljs-selector-tag\">flake8<\/span> .\n<span class=\"hljs-selector-tag\">pytest<\/span> <span class=\"hljs-selector-tag\">-q<\/span>\n\n<span class=\"hljs-selector-tag\">docker<\/span> <span class=\"hljs-selector-tag\">compose<\/span> <span class=\"hljs-selector-tag\">up<\/span> <span class=\"hljs-selector-tag\">-d<\/span>\n<span class=\"hljs-selector-tag\">pytest<\/span> <span class=\"hljs-selector-tag\">-m<\/span> <span class=\"hljs-selector-tag\">integration<\/span>\n<span class=\"hljs-selector-tag\">docker<\/span> <span class=\"hljs-selector-tag\">compose<\/span> <span class=\"hljs-selector-tag\">down<\/span> <span class=\"hljs-selector-tag\">-v<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Common pitfalls and best practices<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Keep early stages fast:<\/strong> Slow first-stage pipelines reduce developer throughput.<\/li><li><strong>Fail early, fail clearly:<\/strong> Surface actionable logs and notifications at the first failing gate.<\/li><li><strong>Avoid overloading end-to-end suites:<\/strong> Reserve them for critical journeys, not all coverage.<\/li><li><strong>Use environment parity:<\/strong> Keep local, CI, and staging behavior as consistent as practical.<\/li><li><strong>Gate promotion intentionally:<\/strong> Require explicit checks before production rollout.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">A strong CI\/CD testing strategy is less about one tool and more about <em>stage design, feedback speed, and risk-based gates<\/em>. Start simple, measure failures by stage, and continuously refine.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A practical guide to designing test stages in a CI\/CD pipeline so teams get fast feedback without slowing delivery. Learn what to test, where to run it, and how to implement it with real pipeline examples.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[2],"tags":[],"class_list":["post-154","post","type-post","status-publish","format-standard","hentry","category-uncategorised"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/154","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=154"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/154\/revisions"}],"predecessor-version":[{"id":79002,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/154\/revisions\/79002"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=154"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=154"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=154"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}