পুরো Friday Waste করেছি Database Layer-এর জন্য "Perfect" Mock Write করতে। Hours Spend করেছি GetByID কী Return করা উচিত Exact Define করতে, শুধু App Production-এ Crash করতে দেখে Missing Comma SQL-এ বা Misunderstood Postgres Constraint-এর জন্য। সেটাই Problem: Mock তোমার Code Test করে না, তোমার Assumptions Test করে। আর সাধারণত, তোমার Assumptions Wrong।

...আর সেই জন্য বেশিরভাগ সময় Testcontainers দিয়ে integration testing করার দিকে Move করেছি। Real Docker Container Spin up-এর দশ সেকেন্ড Wait করা Prefer করি, 80% Sure না এমন Database Behavior Fake করতে দশ মিনিটের চেয়ে।

Mock শুধু Lie যা নিজেকে বলি

তুমি যখন Database Repository Mock করো, তুমি Basically বলছো: "যখন GetByID Call করবে, এই Static Struct Return করো।" এটা Fast, Sure, কিন্তু Reality Completely Ignore করে। Syntax Error Catch করে না, Unique Constraint Violation Catch করে না, আর তোমার Specific Postgres Version JSONB Columns কিভাবে Handle করে সেটা Definitely Catch করে না।

অনেক Project দেখেছি যেখানে Tests 100% Green ছিল কিন্তু Application Core-এ Broken ছিল কারণ Mock Foreign Key Constraint Account করে নি। Real Container-এ Database নিজেই কাজ করে।

CI Headache (আসল Gripe)

দেখো, আমি Testcontainers Love করি, কিন্তু GitHub Actions-এর মতো CI Environment-এ Set up করা Absolute Pain in the neck। Docker-in-Docker (DinD) Configurations, Permission Issues, আর /var/run/docker.sock Mount করা Runner-এ যেটা Actually তোমাকে এতো Power দিতে চায় না — এই Rabbit Hole-এ Down যাওয়া লাগে।

সেই এক Morning থাকে যেখানে CI Pipeline Indefinitely Hang করে কারণ Runner Disk Space Run out হয়ে গেছে postgres:16-alpine Image Pull করতে গিয়ে 100তম বার। এটা Trade-off। "Clean" CI-এর জন্য "Reliable" Code Trade করছি, কিন্তু কেউ বলবেন না এটা একদম সহজ কোনো Setup। এটা Battle।

Go-এ আসলে কিভাবে Use করি

প্রতিটা Test-এর জন্য Container Restart করি না। সেটা Insane হতো। Test Suite-এর শুরুতে একটা Postgres Instance Spin up করি, তারপর প্রতিটা Test-এর জন্য Fresh Database বা Schema Migration Use করি।

func TestRepository_CreateUser(t *testing.T) {
    ctx := context.Background()

    // Real Postgres 16 Container
    pgContainer, err := postgres.RunContainer(ctx,
        testcontainers.WithImage("postgres:16-alpine"),
        postgres.WithWaitStrategy(
            wait.ForLog("database system is ready to accept connections").
                WithOccurrence(2).
                WithStartupTimeout(5*time.Second),
        ),
    )
    if err != nil {
        t.Fatal(err)
    }
    defer pgContainer.Terminate(ctx)

    // এখন আমরা REAL Database-এর Against-এ Test করছি
    connStr, _ := pgContainer.ConnectionString(ctx, "sslmode=disable")
    db, _ := sql.Open("postgres", connStr)
    repo := repository.NewUserRepository(db)

    // এটা ACTUALLY Fail করবে যদি আমার SQL Broken হয়
    err = repo.Create(ctx, &domain.User{Email: "test@example.com"})
    assert.NoError(t, err)
}

Testcontainers integration testing: Speed-এর চেয়ে Confidence বেশি

হ্যাঁ, এটা Slower। কিন্তু দুই মিনিট লাগা Test Suite Prefer করি যেটা আসলে Truth বলে, দুই সেকেন্ডের Suite-এর চেয়ে যেটা মুখে Lie বলে। Hand-crafted SQL Approach Use করার সময়, জানতে হয় আমার Queries Valid কিনা। Testcontainers একমাত্র Way পেয়েছি সেই Confidence পাওয়ার — Actual Staging Environment-এ Deploy না করেই।

Meta Description: কেন মক (Mock) ব্যবহারের চেয়ে Testcontainers দিয়ে integration testing করা ভালো? জানুন ডাটাবেস টেস্টিং-এর বাস্তব অভিজ্ঞতা এবং সিআই (CI) সেটআপের চ্যালেঞ্জগুলো।

Asaduzzaman Pavel

About the Author

Asaduzzaman Pavel is a Software Engineer who actually enjoys the friction of a well-architected system. He has over 15 years of experience building high-performance backends and infrastructure that can actually handle the real-world chaos of scale.

Currently looking for new opportunities to build something amazing.