Asked Aug 19th, 2017 3:23 PM 342 0 2
  • 342 0 2
+3

[Laravel] test database - DatabaseMigrations 🆘

Share
  • 342 0 2

Chào cả nhà.

Mình đang tìm hiểu phpunit, hiện tại mình đang dùng laravel 5.4 và có follow theo hướng dẫn test database tại document chính thức của laravel: https://laravel.com/docs/5.4/database-testing#using-migrations

Mình muốn thực hiện test các file models (sẽ tương tác trực tiếp với database: select, insert, update, delete) theo tài liệu của laravel hướng dẫn thì chỉ cần

use DatabaseMigrations;

là nó sẽ tự chạy migratemigrate:rollback khi chạy test

Vấn đề mình gặp phải là:

trong 1 Class test của mình có 4 function test chẳng hạn thì khi mình gõ lệnh ./vendor/bin/phpunit mỗi lần hàm setUp được gọi là $this->artisan('migrate'); được gọi và sau khi chạy 1 test function nó lại gọi tới $this->artisan('migrate:rollback');

$this->beforeApplicationDestroyed(function () {
            $this->artisan('migrate:rollback');
        });

như vậy là để test được class có 4 function test tôi mà nó gọi tổng cộng là 4 lần $this->artisan('migrate'); và 4 lần $this->artisan('migrate:rollback'); ==> tốn quá nhiều thời gian và tài nguyên trong quá trình chạy test

Mọi người có mình hỏi: có cách nào chỉ cho hàm $this->artisan('migrate'); được gọi khi quá trình test bắt đầu và hàm $this->artisan('migrate:rollback'); được gọi khi quá trình test đã hoàn tất hoặc stop.

2 ANSWERS


Answered Aug 20th, 2017 6:12 AM
Accepted
+3

Đúng như bạn nói, việc dùng trait DatabaseMigrations sẽ dẫn đến việc migrate và rollback sau mỗi test, và nó sẽ không phải là một ý hay khi mà bạn sử dụng CSDL như MySQL. Có một giải pháp đơn giản cho vấn đề này mà mình vẫn đang dùng đó là sử dụng một DB riêng biệt dành cho việc test, trước khi chạy test thì ta chạy migration và seeder nếu cần, và trong các class tests thì sử dụng DatabaseTransactions thay vì DatabaseMigrations. DatabaseTransactions sẽ đơn giản là làm nhiệm vụ tạo ra transaction cho mỗi lần test, rồi rollback lại transaction khi mà test được thực hiện xong. Như vậy sẽ hiệu quả hơn nhiều 😄

Share
Min @gaumin
Aug 20th, 2017 10:53 AM

Cảm ơn @thangtd90 đã trả lời câu hỏi của mình

Nhưng theo mình được biết thì transaction không chạy được với table có engine là MyISAM

0
| Reply
Share
Min @gaumin
Aug 20th, 2017 11:00 AM

Hoặc có cách nào để biết được quá trình test bắt đầu và quá trình test đã xong ko @thangtd90

0
| Reply
Share
Thang Tran Duc @thangtd90
Aug 20th, 2017 2:59 PM

@gaumin Đúng như bạn nói, MyISAM Storage Engine không hỗ trợ transaction, nên nếu hạn chế dùng được thì tốt, chỉ nên dùng InnoDB thôi 😄 Mình không thấy có lý do gì để dùng DatabaseMigrations cả, bởi nó sẽ chạy migrate và rollback trước và sau mỗi test case và điều này là không cần thiết khi ta chỉ cần chạy một lần. Do đó, về vấn đề migrate, bạn có thể chạy manual bằng cách gõ lệnh artisan thôi.

Còn muốn tự động chạy đúng 1 lần khi test (trước khi test bắt đầu) thì mình nghĩ có thể viết ở trong hàm setUp của class TestCase. Có một vấn đề là hàm setUp được gọi trước và sau mỗi hàm test, nên cần có một chút trick để có thể chỉ gọi migration đúng một lần. Bạn thử cách dưới đây xem sao:

# Dùng biến static để lưu trạng thái đã chạy migrate chưa
public static $isMigrated = false;

public function setUp()
{
    parent::setUp();

    if (self::$isMigrated === false) {
        # Nếu chưa chạy migration thì chạy ở đây, sau đó đặt lại giá trị là đã migrate rồi
        self::$isMigrated = true;
    }
}

Còn về việc check xem test hiện tại đã phải là test cuối cùng chưa để mà rollback thì mình cũng chưa nghĩ ra cách (^^;) Hàm trong hàm tearDown thì biến app được clear hoàn toàn, tức là mỗi lần test lại có một biến app mới được tạo ra, nên cũng không thể dựa vào đó mà dùng callback được :-s

Mà thật ra thì mình cũng thấy không cần thiết phải rollback lại sau mỗi lần chạy test làm gì, nếu khi nào cần thì chắc tự gõ manual thôi nhỉ (^^;) P/S: Như mình đã nói ở trên thì bạn nên có một DB riêng biệt dành cho việc test.

0
| Reply
Share
Min @gaumin
Aug 21st, 2017 12:48 AM

một lần nữa cảm ơn @thangtd90 đã gợi ý chi tiết

mình cũng nghĩ y như bạn 😃, tại đang tìm hiểu test nền thích hỏi ra ngô ra khoai ý mà

0
| Reply
Share
Answered Aug 21st, 2017 3:40 AM
+1

Nếu bạn upgrade lên Laravel 5.5 (sẽ được ra mắt trong Laracon EU sắp tới !?) thì bạn có thể dùng RefreshDatabase thay vì DatabaseMigrations hay DatabaseTransactions. Nó sẽ check database driver hiện tại và quyết định sử dụng transaction hay không. Mình thấy nên default về transaction (nếu driver có support), còn chỉ sử dụng migration trong trường hợp driver khá đơn giản như :memory: hay sqlite 😀

Source: https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Testing/RefreshDatabase.php

Share
Min @gaumin
Aug 21st, 2017 4:05 AM

Nhất định mình sẽ phải thử laravel 5.5 vì nó vừa mới release được 2 ngày

Nhưng củ chuối 1 cái là dự án của mình chỉ yêu cầu chạy php 5.6.x thôi 😄

lên laravel 5.5 thì bắt buộc chơi với php 7.x

Laravel 5.5 Server Requirements

PHP >= 7.0.0
OpenSSL PHP Extension
PDO PHP Extension
Mbstring PHP Extension
Tokenizer PHP Extension
XML PHP Extension
0
| Reply
Share
Vinh Nguyen @vinhnguyen
Aug 26th, 2017 7:51 AM

Ok bạn, còn một điều nữa là khi sử dụng RefreshDatabase mình sẽ không cần migrate database một cách thủ công khi có thay đổi về migrations 😃

0
| Reply
Share