টেস্টিং (Professional Quality Assurance)

টেস্টিং আপনার কোডের "সেফটি নেট"। আপনি যখন ৬ মাস পর পুরনো কোডে হাত দেবেন, তখন এই টেস্টগুলোই আপনাকে বলে দেবে— "বস, সব ঠিক আছে" নাকি "কিছু একটা ভেঙেছে"

এই অধ্যায়ে আমরা শিখব কীভাবে প্রফেশনাল এবং অটোমেটেড টেস্ট লিখতে হয়।

১৫.১ PHPUnit — টেস্টিং ফ্রেমওয়ার্কের ভিত্তি

পিএইচপি ইকোসিস্টেমে PHPUnit হলো স্ট্যান্ডার্ড। লারাভেল ডিফল্টভাবে এটি কনফিগার করে দেয়।

📁 ফোল্ডার স্ট্রাকচার এবং phpunit.xml

tests/
 ├── Unit/    (Isolated logic test, Database ছাড়া)
 └── Feature/ (Full HTTP request, Database সহ)
phpunit.xml   (Environment variables কনফিগার করার জায়গা)

💡 Senior Tip: phpunit.xml ফাইলে টেস্টিং ডাটাবেস আলাদা করে নেওয়া ভালো (যেমন: :memory: বা sqlite), যাতে মেইন ডাটাবেসে ইফেক্ট না পড়ে।

১৫.২ ইউনিট টেস্ট (Unit Test) — দ্য মাইক্রো লেভেল

ইউনিট টেস্ট হবে অত্যন্ত ফাস্ট এবং আইসোলেটেড। এখানে কোনো ডাটাবেস কানেকশন, ফাইল সিস্টেম বা নেটওয়ার্ক কল থাকবে না।

উদ্দেশ্য: একটি নির্দিষ্ট মেথড বা লজিক সঠিকভাবে কাজ করছে কি না তা দেখা।

// app/Services/Calculator.php
class Calculator {
    public function add($a, $b) { return $a + $b; }
}

// tests/Unit/CalculatorTest.php
public function test_it_can_add_two_numbers() {
    // 1. Arrange (প্রস্তুতি)
    $calc = new Calculator();
    
    // 2. Act (কাজ)
    $result = $calc->add(10, 20);
    
    // 3. Assert (যাচাই)
    $this->assertEquals(30, $result);
}

Rule: ইউনিট টেস্ট মিলি-সেকেন্ডে রান করতে হবে।

১৫.৩ ফিচার টেস্ট (Feature Test) — রিয়েল সিমুলেশন

ফিচার টেস্ট পুরো রিকোয়েস্ট সাইকেল চেক করে। রাউট → মিডলওয়্যার → কন্ট্রোলার → ডাটাবেস → রেসপন্স।

📦 ডাটাবেস রিসেট (RefreshDatabase)

ফিচার টেস্টে ডাটাবেস ব্যবহার হয়। তাই প্রতিটি টেস্টের পর ডাটাবেস ক্লিন করা জরুরি। লারাভেলে RefreshDatabase ট্রেইট এটি হ্যান্ডেল করে।

use IlluminateFoundationTestingRefreshDatabase;

class UserAuthTest extends TestCase {
    use RefreshDatabase; // প্রতি টেস্ট শেষে ডাটাবেস রোলব্যাক করবে

    public function test_user_can_login_successfully() {
        // 1. Create a user in DB
        $user = User::factory()->create([
            'email' => 'admin@test.com',
            'password' => bcrypt('password'),
        ]);

        // 2. Hit the login endpoint
        $response = $this->postJson('/api/login', [
            'email' => 'admin@test.com',
            'password' => 'password',
        ]);

        // 3. Verify response
        $response->assertStatus(200)
                 ->assertJson(['message' => 'Login success']);
    }
}

১৫.৪ মকিং (Mocking) — ফেইক অবজেক্ট

টেস্টের সময় আমরা রিয়েল পেমেন্ট গেটওয়ে বা ইমেইল সার্ভিস হিট করব না। কারণ:

  • এটা স্লো
  • এতে টাকা কাটতে পারে
  • ইন্টারনেট না থাকলে টেস্ট ফেইল করবে

🔧 ১. এক্সটার্নাল সার্ভিস মক করা

public function test_order_process_calls_payment_gateway() {
    $mock = $this->mock(PaymentGateway::class, function ($mock) {
        $mock->shouldReceive('charge')
             ->once()
             ->with(100)
             ->andReturn(true);
    });

    $response = $this->post('/order', ['amount' => 100]);
}

📦 ২. লারাভেল বিল্ট-ইন ফেইকস (Http, Mail, Queue)

// HTTP Fake
Http::fake([
    'api.stripe.com/*' => Http::response(['status' => 'paid'], 200),
]);

// Mail Fake
Mail::fake();
// ... action ...
Mail::assertSent(OrderShipped::class);

১৫.৫ TDD (Test Driven Development) — উল্টো পথে হাটা

সাধারণত আমরা আগে কোড লিখি, পরে টেস্ট করি। TDD তে উল্টোটা করা হয়।

🔄 TDD সাইকেল (Red-Green-Refactor):

  1. Red: একটি ফেইলিং টেস্ট লিখুন (কারণ কোড এখনো নেই)
  2. Green: টেস্ট পাস করার জন্য মিনিমাম কোড লিখুন
  3. Refactor: কোড সুন্দর করুন, অপ্টিমাইজ করুন (টেস্ট আছে তাই ভয় নেই)

📝 উদাহরণ:

  • Test: test_user_cannot_register_without_email (Red)
  • Code: ভ্যালিডেশন লজিক অ্যাড করলাম (Green)
  • Refactor: ভ্যালিডেশন লজিকটি ফর্ম রিকোয়েস্ট ক্লাসে সরালাম (Green)

১৫.৬ অ্যাসারশনস (The Validation Tools)

টেস্ট পাস নাকি ফেইল—সেটা ঠিক করে অ্যাসারশন। কিছু পাওয়ারফুল অ্যাসারশন:

Assertionকাজ
$this->assertEquals($expected, $actual)ভ্যালু চেক করে
$response->assertStatus(403)HTTP স্ট্যাটাস কোড চেক করে
$response->assertJsonPath('data.id', 5)JSON এর নেস্টেড ভ্যালু চেক করে
$this->assertDatabaseHas('users', [...])ডাটাবেসে রো আছে কি না চেক করে
$this->expectException(ValidationException::class)কোড এরর থ্রো করছে কি না তা নিশ্চিত করে

১৫.৭ ডাটা প্রোভাইডার (Advanced PHPUnit)

একই ফাংশন বিভিন্ন ইনপুট দিয়ে টেস্ট করার জন্য বারবার ফাংশন না লিখে Data Provider ব্যবহার করা সিনিয়রের লক্ষণ।

/**
 * @dataProvider emailProvider
 */
public function test_email_validation($email, $isValid) {
    $validator = Validator::make(['email' => $email], ['email' => 'email']);
    $this->assertEquals($isValid, $validator->passes());
}

public function emailProvider() {
    return [
        ['test@example.com', true],  // Case 1
        ['invalid-email', false],    // Case 2
        ['@domain.com', false],      // Case 3
    ];
}

১৫.৮ বেস্ট প্র্যাকটিস (Senior Guidelines)

  • 📐AAA Pattern (Arrange, Act, Assert): প্রতিটি টেস্ট ফাংশনকে ৩ ভাগে ভাগ করুন। এতে কোড রিডেবল হয়।
  • 🏷️Descriptive Naming: টেস্টের নাম বড় হলে সমস্যা নেই, স্পেস বা আন্ডারস্কোর দিয়ে বিস্তারিত লিখুন। যেমন: test_user_is_redirected_to_dashboard_after_login
  • 🚫Don't Test Framework: লারাভেলের belongsTo কাজ করে কি না তা টেস্ট করার দরকার নেই। আপনার বিজনেস লজিক টেস্ট করুন।
  • 🔄CI/CD Integration: গিটহাবে পুশ করার সাথে সাথে অটোমেটিক টেস্ট রান করুন (GitHub Actions)

🎯 Senior Developer Interview Questions (Chapter 15)

Q: ইউনিট টেস্ট এবং ফিচার টেস্টের মূল পার্থক্য কী? কখন কোনটি লিখবেন?

ইউনিট টেস্ট আইসোলেটেড লজিক (যেমন ক্লাস মেথড) টেস্ট করে, কোনো ডাটাবেস বা নেটওয়ার্ক কল ছাড়া। ফিচার টেস্ট পুরো রিকোয়েস্ট ফ্লো (এন্ডপয়েন্ট) টেস্ট করে। বিজনেস লজিকের জন্য ইউনিট, আর API/Web রাউটের জন্য ফিচার টেস্ট লেখা উচিত।

Q: TDD এর রেড-গ্রিন-রিফ্যাক্টর সাইকেল ব্যাখ্যা করুন।

প্রথমে টেস্ট লেখা যা ফেইল করবে (Red), এরপর সেই টেস্ট পাস করানোর জন্য কোড লেখা (Green), এবং সবশেষে কোড ক্লিন ও অপ্টিমাইজ করা (Refactor)।

Q: মকিং (Mocking) কেন জরুরি? একটি রিয়েল-লাইফ উদাহরণ দিন।

এক্সটার্নাল ডিপেন্ডেন্সি (যেমন পেমেন্ট গেটওয়ে, ইমেইল) ছাড়াই সিস্টেম টেস্ট করার জন্য। যেমন: স্ট্রাইপ এপিআই মক করা যাতে টেস্টের সময় সত্যিকারের ডলার চার্জ না হয়।

Q: RefreshDatabase ট্রেইটটি কী কাজ করে?

এটি প্রতি টেস্টের আগে বা পরে ডাটাবেস মাইগ্রেশন রোলব্যাক করে বা ট্রানজেকশন রিসেট করে, যাতে এক টেস্টের ডাটা অন্য টেস্টকে প্রভাবিত না করে।

🎯 অধ্যায় ১৫ এর সারাংশ (Summary)

এই অধ্যায়ে আমরা শিখলাম:

  • PHPUnit: Standard testing framework for PHP
  • Unit Test: Isolated logic, no DB, no network, must be fast
  • Feature Test: Full HTTP request cycle, uses RefreshDatabase trait
  • Mocking: Fake external dependencies (Payment, Mail, HTTP)
  • TDD: Red → Green → Refactor cycle
  • Data Providers: Test same logic with multiple inputs

✨ আপনার কোড এখন শুধু কাজই করে না, এটি প্রমাণিত এবং প্রোডাকশন-রেডি

🚀 PHP Senior Journey: 15/15 Chapters Completed! 🎉

🏆 Congratulations! You are now a Senior PHP Engineer & Architect!

🔒 কপিরাইট সুরক্ষিত কন্টেন্ট 🔒

কপি, স্ক্রিনশট, প্রিন্ট করা সম্পূর্ণ নিষিদ্ধ।