Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Database Mock Laravel Package

mpyw/laravel-database-mock

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package in your Laravel 11/12 project:
    composer require mpyw/laravel-database-mock mpyw/mockery-pdo:dev-alpha
    
  2. Enable Mockery in your test environment (e.g., phpunit.xml):
    <php>
        <env name="MOCKERY" value="1"/>
    </php>
    
  3. First mock usage in a test:
    use Mpyw\LaravelDatabaseMock\Facades\DBMock;
    
    public function test_user_creation()
    {
        $pdo = DBMock::mockPdo();
        $pdo->shouldInsert(
            'insert into `users` (...) values (?, ?, ?)',
            ['John', 'john@example.com', '2023-01-01 00:00:00']
        )->shouldReturn(1);
    
        $user = User::create([...]);
        $this->assertEquals(1, $user->id);
    }
    

First Use Case: Isolated Unit Testing

Replace a flaky database-dependent test with a mocked query:

public function test_user_query()
{
    $pdo = DBMock::mockPdo();
    $pdo->shouldSelect('select * from users where id = ?')
        ->shouldFetchAllReturns([['id' => 1, 'name' => 'John']]);

    $user = User::find(1);
    $this->assertEquals('John', $user->name);
}

Implementation Patterns

Core Workflow: Mock-Driven Testing

  1. Setup Phase:
    public function setUp(): void
    {
        parent::setUp();
        $this->pdo = DBMock::mockPdo(); // Global mock for all tests
    }
    
  2. Define Expectations (before assertions):
    $this->pdo->shouldSelect('SELECT * FROM orders WHERE user_id = ?')
        ->shouldFetchAllReturns([['id' => 1, 'amount' => 100]]);
    
  3. Execute Test Logic:
    $orders = Order::where('user_id', 1)->get();
    $this->assertCount(1, $orders);
    
  4. Verify Side Effects (if needed):
    $this->pdo->shouldHaveReceived('insert');
    

Integration with Laravel Features

Eloquent Relationships

public function test_user_with_posts()
{
    $pdo = DBMock::mockPdo();
    $pdo->shouldSelect('SELECT * FROM users WHERE id = ?')
        ->shouldFetchAllReturns([['id' => 1, 'name' => 'John']]);
    $pdo->shouldSelect('SELECT * FROM posts WHERE user_id = ?')
        ->shouldFetchAllReturns([['id' => 1, 'title' => 'Hello']]);

    $user = User::with('posts')->find(1);
    $this->assertEquals('Hello', $user->posts[0]->title);
}

Read/Write Replicas

public function test_read_write_separation()
{
    $pdos = DBMock::mockEachPdo();
    $pdos->reader()->shouldSelect('SELECT * FROM products')
        ->shouldFetchAllReturns([['id' => 1, 'name' => 'Laptop']]);
    $pdos->writer()->shouldInsert('INSERT INTO orders (...)')
        ->shouldReturn(1);

    $product = Product::first();
    Order::create([...]);
}

Transactions

public function test_transaction_rollback()
{
    $pdo = DBMock::mockPdo();
    $pdo->shouldBeginTransaction();
    $pdo->shouldInsert('INSERT INTO logs (...)')
        ->shouldThrow(new \Exception('DB Error'));

    $this->expectException(\Exception::class);
    DB::transaction(function () {
        Log::create([...]);
    });
}

Advanced Patterns

Dynamic Query Mocking

public function test_dynamic_where_clause()
{
    $pdo = DBMock::mockPdo();
    $pdo->shouldReceive('prepare')
        ->with('SELECT * FROM users WHERE name = ?')
        ->andReturnSelf();
    $pdo->shouldReceive('execute')
        ->with(['John'])
        ->andReturnSelf();
    $pdo->shouldReceive('fetchAll')
        ->andReturn([['id' => 1, 'name' => 'John']]);

    $user = User::where('name', 'John')->first();
    $this->assertEquals(1, $user->id);
}

Error Simulation

public function test_database_error_handling()
{
    $pdo = DBMock::mockPdo();
    $pdo->shouldSelect('SELECT * FROM invalid_table')
        ->shouldThrow(new \PDOException('SQLSTATE[42S02]: Base table'));

    $this->expectException(\PDOException::class);
    User::where('id', 1)->first();
}

Time Travel with Carbon

public function test_timestamp_mocking()
{
    Carbon::setTestNow('2023-01-01 12:00:00');
    $pdo = DBMock::mockPdo();
    $pdo->shouldInsert('INSERT INTO events (...)')
        ->shouldReturn(1);

    $event = Event::create([...]);
    $this->assertEquals('2023-01-01 12:00:00', $event->created_at->format('Y-m-d H:i:s'));
}

Gotchas and Tips

Common Pitfalls

  1. Query Mismatch Errors

    • Issue: Mocked query strings must exactly match the real query (including whitespace, backticks, and parameter placeholders).
    • Fix: Use DB::enableQueryLog() to inspect real queries:
      DB::enableQueryLog();
      User::all();
      dd(DB::getQueryLog()); // Copy the exact query for mocking
      
  2. Parameter Binding Order

    • Issue: shouldInsert() expects parameters in the same order as the query.
    • Fix: Reorder parameters or use named placeholders (if supported):
      $pdo->shouldInsert('INSERT INTO users (name, email) VALUES (?, ?)', ['John', 'john@example.com']);
      
  3. Global vs. Per-Test Mocks

    • Issue: Global mocks (DBMock::mockPdo()) persist across tests, causing flakiness.
    • Fix: Mock per test or reset expectations:
      public function tearDown(): void
      {
          DBMock::reset();
          parent::tearDown();
      }
      
  4. Carbon Timestamp Conflicts

    • Issue: Carbon::setTestNow() may conflict with other time-mocking tools (e.g., PestPHP).
    • Fix: Reset Carbon after tests:
      public function tearDown(): void
      {
          Carbon::setTestNow(null);
          parent::tearDown();
      }
      
  5. Read/Write Replica Misconfiguration

    • Issue: Forgetting to mock both reader and writer connections.
    • Fix: Always use DBMock::mockEachPdo() for replica setups.
  6. Alpha Dependency Risks

    • Issue: mockery-pdo is in alpha; may introduce breaking changes.
    • Fix: Pin to a specific version and monitor for updates:
      composer require mpyw/mockery-pdo:dev-alpha#1.0.0-alpha1
      

Debugging Tips

  1. Inspect Mocked Queries

    • Use Mockery’s getMock() to debug:
      $mock = DBMock::mockPdo()->getMock();
      var_dump($mock->getInvocations());
      
  2. Verify Expectations

    • Check if expectations were met:
      $this->assertTrue($pdo->shouldHaveReceived('select'));
      
  3. Log Unmocked Queries

    • Add a global listener to catch unexpected queries:
      DB::listen(function ($query) {
          if (!str_contains($query->sql, 'mocked_query')) {
              $this->fail("Unexpected query: " . $query->sql);
          }
      });
      
  4. Handle Dynamic SQL

    • For dynamic queries (e.g., DB::raw()), mock the PDO layer directly:
      $pdo = DBMock::mockPdo();
      $pdo->shouldReceive('prepare')
          ->with('SELECT * FROM users WHERE id = :id')
          ->andReturnSelf();
      $pdo->shouldReceive('execute')
          ->with(['id' => 1])
          ->andReturnSelf();
      $pdo->shouldReceive('fetchAll')
          ->andReturn([['id' => 1, 'name' => 'John']]);
      

Configuration Quirks

  1. Connection-Specific Mocking
    • Mock specific connections by name:
      DBMock::mockPdo
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky