() translation by (you can also view the original English article)
Jika Anda berharap untuk mempelajari mengapa tes bermanfaat, ini bukanlah artikel untuk Anda. Selama tutorial ini, saya akan berasumsi bahwa Anda sudah memahami keuntungan, dan berharap untuk belajar cara terbaik untuk menulis dan mengatur tes Anda dalam Laravel 4.
Versi 4 Laravel menawarkan perbaikan serius dalam kaitannya dengan testing, bila dibandingkan dengan rilis sebelumnya. Ini adalah artikel pertama dari seri yang akan mencakup bagaimana untuk menulis tes untuk aplikasi Laravel 4. Kami akan mulai seri dengan membahas model testing.
Setup
In-memory database
Kecuali Anda menjalankan raw query pada database Anda, Laravel memungkinkan aplikasi Anda untuk tetap agnostik database. Dengan perubahan sederhana driver, aplikasi Anda sekarang dapat bekerja dengan DBMS yang lain (MySQL, PostgreSQL, SQLite, dll). Di antara pilihan default, SQLite menawarkan fitur yang aneh, namun sangat berguna: database di memori.
Dengan Sqlite, kita dapat mengatur koneksi database: memori:
, yang secara drastis akan mempercepat tes kami, karena database tidak ada pada hard disk. Selain itu, produksi pengembangan database akan pernah diisi dengan data uji kiri-atas, karena sambungan,: memori:
, selalu dimulai dengan database kosong.
Singkatnya: in-memory database memungkinkan untuk tes cepat dan bersih.
Dalam direktori app/config/testing
, membuat file baru, bernama database.ph
p, dan mengisinya dengan konten berikut:
1 |
// app/config/testing/database.php |
2 |
|
3 |
<?php
|
4 |
|
5 |
return array( |
6 |
|
7 |
'default' => 'sqlite', |
8 |
|
9 |
'connections' => array( |
10 |
'sqlite' => array( |
11 |
'driver' => 'sqlite', |
12 |
'database' => ':memory:', |
13 |
'prefix' => '' |
14 |
),
|
15 |
)
|
16 |
);
|
Fakta bahwa database.php
ditempatkan dalam konfigurasi testing
direktori berarti bahwa pengaturan ini hanya akan digunakan ketika di lingkungan testing (yang Laravel secara otomatis mengatur). Dengan demikian, ketika aplikasi yang diakses biasanya, database di memori tidak akan digunakan.
Sebelum menjalankan tes
Karena in-memory database selalu kosong saat sambungan dibuat, sangat penting untuk bermigrasi database sebelum setiap tes. Untuk melakukannya, buka app/tests/TestCase.php dan menambahkan metode berikut ke akhir kelas:
1 |
/**
|
2 |
* Migrates the database and set the mailer to 'pretend'.
|
3 |
* This will cause the tests to run quickly.
|
4 |
*
|
5 |
*/
|
6 |
private function prepareForTests() |
7 |
{
|
8 |
Artisan::call('migrate'); |
9 |
Mail::pretend(true); |
10 |
}
|
Catatan: Metode
setUp()
yang dijalankan oleh PHPUnit sebelum setiap tes.
Metode ini akan menyiapkan database, dan mengubah status Laravel's Mailer
kelas untuk berpura-pura
. Dengan cara ini, Mailer tidak akan mengirim email nyata ketika menjalankan tes. Sebaliknya, itu akan log "Kirim".
Untuk menyelesaikan app/tests/TestCase.php,
panggilan prepareForTests()
dalam PHPUnit setUp()
metode, yang akan mengeksekusi sebelum setiap tes.
Jangan lupa
parent::setUp()
, seperti kita sedang menimpa metode parent class.
1 |
/**
|
2 |
* Default preparation for each test
|
3 |
*
|
4 |
*/
|
5 |
public function setUp() |
6 |
{
|
7 |
parent::setUp(); // Don't forget this! |
8 |
|
9 |
$this->prepareForTests(); |
10 |
}
|
Pada titik ini, app/tests/TestCase.php
akan tampak seperti kode berikut. Ingat bahwa createApplication
dibuat secara otomatis oleh Laravel. Anda tidak perlu khawatir tentang hal itu.
1 |
// app/tests/TestCase.php |
2 |
|
3 |
<?php
|
4 |
|
5 |
class TestCase extends Illuminate\Foundation\Testing\TestCase { |
6 |
|
7 |
/**
|
8 |
* Default preparation for each test
|
9 |
*/
|
10 |
public function setUp() |
11 |
{
|
12 |
parent::setUp(); |
13 |
|
14 |
$this->prepareForTests(); |
15 |
}
|
16 |
|
17 |
/**
|
18 |
* Creates the application.
|
19 |
*
|
20 |
* @return Symfony\Component\HttpKernel\HttpKernelInterface
|
21 |
*/
|
22 |
public function createApplication() |
23 |
{
|
24 |
$unitTesting = true; |
25 |
|
26 |
$testEnvironment = 'testing'; |
27 |
|
28 |
return require __DIR__.'/../../start.php'; |
29 |
}
|
30 |
|
31 |
/**
|
32 |
* Migrates the database and set the mailer to 'pretend'.
|
33 |
* This will cause the tests to run quickly.
|
34 |
*/
|
35 |
private function prepareForTests() |
36 |
{
|
37 |
Artisan::call('migrate'); |
38 |
Mail::pretend(true); |
39 |
}
|
40 |
}
|
Sekarang, untuk menulis kami tes, hanya extend TestCase
, dan database akan diinisialisasi dan bermigrasi sebelum setiap tes.
Tes
Memang benar untuk mengatakan bahwa, dalam artikel ini, kami tidak akan mengikuti proses TDD. Masalahnya di sini didaktik, dengan tujuan untuk menunjukkan bagaimana tes dapat ditulis. Karena ini, saya memilih untuk mengungkapkan model dalam pertanyaan pertama, dan kemudian mereka tes yang terkait. Saya percaya bahwa ini adalah cara yang lebih baik untuk menggambarkan tutorial ini.
Konteks dari aplikasi demo ini adalah sebuah blog/CMS sederhana, yang mengandung pengguna (otentikasi), posting dan halaman statis (yang ditampilkan dalam menu).
Post model
Harap dicatat bahwa model extend kelas, Ardent, daripada Eloquent. Ardent adalah paket yang membuat untuk validasi mudah, saat menyimpan model (Lihat properti $rules
).
Selanjutnya, kami memiliki array public statis $factory
, yang memanfaatkan paket FactoryMuff, untuk membantu dengan pembuatan obyek ketika testing.
Ardentx dan FactoryMuff juga tersedia melalui Packagist dan composer.
Dalam model Post
kami, kami memiliki relationship dengan model User
, melalui metode author
magic.
Akhirnya, kami memiliki sebuah metode sederhana yang mengembalikan tanggal, diformat sebagai "hari/bulan/tahun".
1 |
// app/models/Post.php |
2 |
|
3 |
<?php
|
4 |
|
5 |
use LaravelBook\Ardent\Ardent; |
6 |
|
7 |
class Post extends Ardent { |
8 |
|
9 |
/**
|
10 |
* Table
|
11 |
*/
|
12 |
protected $table = 'posts'; |
13 |
|
14 |
/**
|
15 |
* Ardent validation rules
|
16 |
*/
|
17 |
public static $rules = array( |
18 |
'title' => 'required', // Post tittle |
19 |
'slug' => 'required|alpha_dash', // Post Url |
20 |
'content' => 'required', // Post content (Markdown) |
21 |
'author_id' => 'required|numeric', // Author id |
22 |
);
|
23 |
|
24 |
/**
|
25 |
* Array used by FactoryMuff to create Test objects
|
26 |
*/
|
27 |
public static $factory = array( |
28 |
'title' => 'string', |
29 |
'slug' => 'string', |
30 |
'content' => 'text', |
31 |
'author_id' => 'factory|User', // Will be the id of an existent User. |
32 |
);
|
33 |
|
34 |
/**
|
35 |
* Belongs to user
|
36 |
*/
|
37 |
public function author() |
38 |
{
|
39 |
return $this->belongsTo( 'User', 'author_id' ); |
40 |
}
|
41 |
|
42 |
/**
|
43 |
* Get formatted post date
|
44 |
*
|
45 |
* @return string
|
46 |
*/
|
47 |
public function postedAt() |
48 |
{
|
49 |
$date_obj = $this->created_at; |
50 |
|
51 |
if (is_string($this->created_at)) |
52 |
$date_obj = DateTime::createFromFormat('Y-m-d H:i:s', $date_obj); |
53 |
|
54 |
return $date_obj->format('d/m/Y'); |
55 |
}
|
56 |
}
|
Post test
Untuk menjaga hal-hal yang terorganisir, saya sudah ditempatkan kelas dengan tes model Post
di app/tests/models/PostTest.php
. Kita melalui semua tes, satu bagian pada suatu waktu.
1 |
// app/tests/models/PostTest.php |
2 |
|
3 |
<?php
|
4 |
|
5 |
use Zizaco\FactoryMuff\Facade\FactoryMuff; |
6 |
|
7 |
class PostTest extends TestCase |
8 |
{
|
Kami extend beberapa TestCase
kelas, yang merupakan persyaratan untuk PHPUnit di Laravel. Juga, jangan lupa kami metode prepareTests
yang akan berjalan sebelum setiap tes.
1 |
public function test_relation_with_author() |
2 |
{
|
3 |
// Instantiate, fill with values, save and return
|
4 |
$post = FactoryMuff::create('Post'); |
5 |
|
6 |
// Thanks to FactoryMuff, this $post have an author
|
7 |
$this->assertEquals( $post->author_id, $post->author->id ); |
8 |
}
|
Tes ini adalah salah satu yang "opsional". Kami menguji bahwa hubungan "Post
dimilik oleh User
". Tujuan di sini adalah sebagian besar untuk menunjukkan fungsi FactoryMuff.
Setelah kelas Post
memiliki $factory
statis array yang berisi 'author_id
' => ' factory | User
' (Perhatikan kode sumber dari model, ditampilkan di atas) FactoryMuff instantiate mengisi User baru atributnya, menyimpan dalam database dan akhirnya kembali dengan id untuk atribut author_id
dalam Post
.
Untuk ini menjadi mungkin, User
model harus memiliki array $factory
yang menggambarkan kolom.
Perhatikan bagaimana Anda dapat mengakses relasi User
melalui $post->author
. Sebagai contoh, kita dapat mengakses $post->author->username,
atau atribut pengguna apapun lainnya yang sudah ada.
Package FactoryMuff memungkinkan cepat Instansiasi object-object yang konsisten untuk tujuan testubg, tetap menghargai dan instantiating hubungan yang diperlukan. Dalam kasus ini, ketika kita membuat Post
dengan FactoryMuff::create('Post')
User
akan juga disiapkan dan dibuat tersedia.
1 |
public function test_posted_at() |
2 |
{
|
3 |
// Instantiate, fill with values, save and return
|
4 |
$post = FactoryMuff::create('Post'); |
5 |
|
6 |
// Regular expression that represents d/m/Y pattern
|
7 |
$expected = '/\d{2}\/\d{2}\/\d{4}/'; |
8 |
|
9 |
// True if preg_match finds the pattern
|
10 |
$matches = ( preg_match($expected, $post->postedAt()) ) ? true : false; |
11 |
|
12 |
$this->assertTrue( $matches ); |
13 |
}
|
14 |
}
|
Untuk menyelesaikan, kita menentukan jika string kembali dengan metode postedAt()
mengikuti format "hari/bulan/tahun". Untuk verifikasi seperti itu, kalimat biasa digunakan untuk menguji jika pola \d{2}\/\d{2}\/\d{4}
("2 nomor" + "bar" + "2 nomor" + "bar" + "4 angka") ditemukan.
Selain itu, kita bisa menggunakan PHPUnit's assertRegExp matcher.
Pada titik ini, app/tests/models/PostTest.php
file adalah sebagai berikut:
1 |
// app/tests/models/PostTest.php |
2 |
|
3 |
<?php
|
4 |
|
5 |
use Zizaco\FactoryMuff\Facade\FactoryMuff; |
6 |
|
7 |
class PostTest extends TestCase |
8 |
{
|
9 |
public function test_relation_with_author() |
10 |
{
|
11 |
// Instantiate, fill with values, save and return
|
12 |
$post = FactoryMuff::create('Post'); |
13 |
|
14 |
// Thanks to FactoryMuff this $post have an author
|
15 |
$this->assertEquals( $post->author_id, $post->author->id ); |
16 |
}
|
17 |
|
18 |
public function test_posted_at() |
19 |
{
|
20 |
// Instantiate, fill with values, save and return
|
21 |
$post = FactoryMuff::create('Post'); |
22 |
|
23 |
// Regular expression that represents d/m/Y pattern
|
24 |
$expected = '/\d{2}\/\d{2}\/\d{4}/'; |
25 |
|
26 |
// True if preg_match finds the pattern
|
27 |
$matches = ( preg_match($expected, $post->postedAt()) ) ? true : false; |
28 |
|
29 |
$this->assertTrue( $matches ); |
30 |
}
|
31 |
}
|
PS: saya memilih untuk tidak menulis nama tes di CamelCase untuk tujuan terbaca. PSR-1 Maafkan aku, tapi
testRelationWithAuthor
tidak dapat dibaca sebagai saya pribadi lebih suka. Anda bebas untuk menggunakan gaya yang Anda paling inginkan, tentu saja.
Page model
CMS kita membutuhkan model untuk mewakili halaman statis. Model ini implementasi sebagai berikut:
1 |
<?php
|
2 |
|
3 |
// app/models/Page.php
|
4 |
|
5 |
use LaravelBook\Ardent\Ardent; |
6 |
|
7 |
class Page extends Ardent { |
8 |
|
9 |
/**
|
10 |
* Table
|
11 |
*/
|
12 |
protected $table = 'pages'; |
13 |
|
14 |
/**
|
15 |
* Ardent validation rules
|
16 |
*/
|
17 |
public static $rules = array( |
18 |
'title' => 'required', // Page Title |
19 |
'slug' => 'required|alpha_dash', // Slug (url) |
20 |
'content' => 'required', // Content (markdown) |
21 |
'author_id' => 'required|numeric', // Author id |
22 |
);
|
23 |
|
24 |
/**
|
25 |
* Array used by FactoryMuff
|
26 |
*/
|
27 |
public static $factory = array( |
28 |
'title' => 'string', |
29 |
'slug' => 'string', |
30 |
'content' => 'text', |
31 |
'author_id' => 'factory|User', // Will be the id of an existent User. |
32 |
);
|
33 |
|
34 |
/**
|
35 |
* Belongs to user
|
36 |
*/
|
37 |
public function author() |
38 |
{
|
39 |
return $this->belongsTo( 'User', 'author_id' ); |
40 |
}
|
41 |
|
42 |
/**
|
43 |
* Renders the menu using cache
|
44 |
*
|
45 |
* @return string Html for page links.
|
46 |
*/
|
47 |
public static function renderMenu() |
48 |
{
|
49 |
$pages = Cache::rememberForever('pages_for_menu', function() |
50 |
{
|
51 |
return Page::select(array('title','slug'))->get()->toArray(); |
52 |
});
|
53 |
|
54 |
$result = ''; |
55 |
|
56 |
foreach( $pages as $page ) |
57 |
{
|
58 |
$result .= HTML::action( 'PagesController@show', $page['title'], ['slug'=>$page['slug']] ).' | '; |
59 |
}
|
60 |
|
61 |
return $result; |
62 |
}
|
63 |
|
64 |
/**
|
65 |
* Forget cache when saved
|
66 |
*/
|
67 |
public function afterSave( $success ) |
68 |
{
|
69 |
if( $success ) |
70 |
Cache::forget('pages_for_menu'); |
71 |
}
|
72 |
|
73 |
/**
|
74 |
* Forget cache when deleted
|
75 |
*/
|
76 |
public function delete() |
77 |
{
|
78 |
parent::delete(); |
79 |
Cache::forget('pages_for_menu'); |
80 |
}
|
81 |
|
82 |
}
|
Kita dapat amati bahwa metode statis, renderMenu(
), menjadikan jumlah link semua halaman yang sudah ada. Nilai ini disimpan dalam cache kunci, 'pages_for_menu'
. Dengan cara ini, nanti kita panggal ke renderMenu()
, akan ada tidak perlu hit database asli. Ini dapat memberikan peningkatan yang signifikan untuk kinerja aplikasi kami.
Namun, jika Page
yang disimpan atau dihapus (metode afterSave()
dan delete()
), nilai cache akan dihapus, menyebabkan renderMenu()
untuk mencerminkan keadaan baru database. Jadi, jika nama halaman berubah, atau jika akan dihapus, key 'pages_for_menu'
dihapus dari cache. (Cache::forget('pages_for_menu');
)
Catatan: Metode,
afterSave()
, tersedia melalui Ardent package. Jika tidak, maka akan diperlukan untuk menerapkan methodsave()
untuk membersihkan cache dan memanggilparent::save();
Page Test
Di: app/tests/models/PageTest.php
, kita akan menulis testing berikut:
1 |
<?php
|
2 |
|
3 |
// app/tests/models/PageTest.php
|
4 |
|
5 |
use Zizaco\FactoryMuff\Facade\FactoryMuff; |
6 |
|
7 |
class PageTest extends TestCase |
8 |
{
|
9 |
public function test_get_author() |
10 |
{
|
11 |
$page = FactoryMuff::create('Page'); |
12 |
|
13 |
$this->assertEquals( $page->author_id, $page->author->id ); |
14 |
}
|
Sekali lagi, kami memiliki tes "opsional" untuk mengkonfirmasi relasi. Karena hubungan tanggung jawab Illuminate\Database\Eloquent
, yang telah ditutupi oleh Laravel sendiri tes, kita tidak perlu untuk menulis tes lain untuk mengkonfirmasi bahwa kode ini bekerja seperti yang diharapkan.
1 |
public function test_render_menu() |
2 |
{
|
3 |
$pages = array(); |
4 |
|
5 |
for ($i=0; $i < 4; $i++) { |
6 |
$pages[] = FactoryMuff::create('Page'); |
7 |
}
|
8 |
|
9 |
$result = Page::renderMenu(); |
10 |
|
11 |
foreach ($pages as $page) |
12 |
{
|
13 |
// Check if each page slug(url) is present in the menu rendered.
|
14 |
$this->assertGreaterThan(0, strpos($result, $page->slug)); |
15 |
}
|
16 |
|
17 |
// Check if cache has been written
|
18 |
$this->assertNotNull(Cache::get('pages_for_menu')); |
19 |
}
|
Ini adalah salah satu paling penting tes untuk model Page
. Pertama, empat halaman dibuat dalam for
loop. Setelah itu, hasil dari panggilan renderMenu()
disimpan dalam variabel $result
. Variabel ini harus berisi string HTML, yang berisi link ke halaman yang sudah ada.
Foreach
loop memeriksa apakah slug (url) setiap halaman di $result
. Ini adalah cukup, karena format yang sama persis dari HTML tidak relevan dengan kebutuhan kita.
Akhirnya, kita menentukan apakah cache kunci, pages_for_menu
, memiliki sesuatu yang disimpan. Dengan kata lain, renderMenu()
panggilan benar-benar disimpan beberapa nilai ke cache?
1 |
public function test_clear_cache_after_save() |
2 |
{
|
3 |
// An test value is saved in cache
|
4 |
Cache::put('pages_for_menu','avalue', 5); |
5 |
|
6 |
// This should clean the value in cache
|
7 |
$page = FactoryMuff::create('Page'); |
8 |
|
9 |
$this->assertNull(Cache::get('pages_for_menu')); |
10 |
}
|
Tes ini bertujuan untuk memverifikasi jika, saat menyimpan Page
baru, tombol cache 'pages_for_menu
' dikosongkan. FactoryMuff::create('Page');
akhirnya memicu save()
method, sehingga yang harus cukup untuk kunci, 'pages_for_menu',
harus dibersihkan.
1 |
public function test_clear_cache_after_delete() |
2 |
{
|
3 |
$page = FactoryMuff::create('Page'); |
4 |
|
5 |
// An test value is saved in cache
|
6 |
Cache::put('pages_for_menu','value', 5); |
7 |
|
8 |
// This should clean the value in cache
|
9 |
$page->delete(); |
10 |
|
11 |
$this->assertNull(Cache::get('pages_for_menu')); |
12 |
}
|
Mirip dengan tes sebelumnya, yang satu ini menentukan jika tombol 'pages_for_menu
' dikosongkan benar setelah menghapus Page
.
PageTest.php
Anda akan terlihat seperti:
1 |
<?php
|
2 |
|
3 |
// app/tests/models/PageTest.php
|
4 |
|
5 |
use Zizaco\FactoryMuff\Facade\FactoryMuff; |
6 |
|
7 |
class PageTest extends TestCase |
8 |
{
|
9 |
public function test_get_author() |
10 |
{
|
11 |
$page = FactoryMuff::create('Page'); |
12 |
|
13 |
$this->assertEquals( $page->author_id, $page->author->id ); |
14 |
}
|
15 |
|
16 |
public function test_render_menu() |
17 |
{
|
18 |
$pages = array(); |
19 |
|
20 |
for ($i=0; $i < 4; $i++) { |
21 |
$pages[] = FactoryMuff::create('Page'); |
22 |
}
|
23 |
|
24 |
$result = Page::renderMenu(); |
25 |
|
26 |
foreach ($pages as $page) |
27 |
{
|
28 |
// Check if each page slug(url) is present in the menu rendered.
|
29 |
$this->assertGreaterThan(0, strpos($result, $page->slug)); |
30 |
}
|
31 |
|
32 |
// Check if cache has been written
|
33 |
$this->assertNotNull(Cache::get('pages_for_menu')); |
34 |
}
|
35 |
|
36 |
public function test_clear_cache_after_save() |
37 |
{
|
38 |
// An test value is saved in cache
|
39 |
Cache::put('pages_for_menu','avalue', 5); |
40 |
|
41 |
// This should clean the value in cache
|
42 |
$page = FactoryMuff::create('Page'); |
43 |
|
44 |
$this->assertNull(Cache::get('pages_for_menu')); |
45 |
}
|
46 |
|
47 |
public function test_clear_cache_after_delete() |
48 |
{
|
49 |
$page = FactoryMuff::create('Page'); |
50 |
|
51 |
// An test value is saved in cache
|
52 |
Cache::put('pages_for_menu','value', 5); |
53 |
|
54 |
// This should clean the value in cache
|
55 |
$page->delete(); |
56 |
|
57 |
$this->assertNull(Cache::get('pages_for_menu')); |
58 |
}
|
59 |
}
|
User model
Terkait dengan model sebelumnya, kami sekarang memiliki User
. Berikut adalah kode untuk model:
1 |
<?php
|
2 |
|
3 |
// app/models/User.php
|
4 |
|
5 |
use Zizaco\Confide\ConfideUser; |
6 |
|
7 |
class User extends ConfideUser { |
8 |
|
9 |
// Array used in FactoryMuff
|
10 |
public static $factory = array( |
11 |
'username' => 'string', |
12 |
'email' => 'email', |
13 |
'password' => '123123', |
14 |
'password_confirmation' => '123123', |
15 |
);
|
16 |
|
17 |
/**
|
18 |
* Has many pages
|
19 |
*/
|
20 |
public function pages() |
21 |
{
|
22 |
return $this->hasMany( 'Page', 'author_id' ); |
23 |
}
|
24 |
|
25 |
/**
|
26 |
* Has many posts
|
27 |
*/
|
28 |
public function posts() |
29 |
{
|
30 |
return $this->hasMany( 'Post', 'author_id' ); |
31 |
}
|
32 |
|
33 |
}
|
Model ini tidak ada tes.
Kita dapat mengamati bahwa, dengan pengecualian dari relasi (yang dapat membantu untuk menguji), tidak ada setiap metode pelaksanaan di sini. Gimana tentang otentikasi? Yah, penggunaan package Confide sudah menyediakan implementasi dan tes untuk ini.
Test untuk
Zizaco\Confide\ConfideUser
terletak di ConfideUserTest.php.
Hal ini penting untuk menentukan tanggung jawab kelas sebelum menulis tes Anda. Pengujian pilihan untuk "reset password" User
akan redundant. Hal ini karena tanggung-jawab yang tepat untuk tes ini dalam Zizaco\Confide\ConfideUser
. bukan di User
Hal yang sama juga berlaku untuk tes validasi data. Sebagai paket, Ardent, menangani tanggung jawab ini, itu tidak akan membuat banyak akal untuk menguji fungsionalitas lagi.
Singkatnya: menjaga tes Anda bersih dan terorganisir. Menentukan tanggung-jawab yang tepat dari setiap kelas, dan menguji hanya apa adalah benar-benar tanggung jawabnya.
Kesimpulan

Menggunakanin-memory database adalah praktik yang baik untuk melaksanakan tes terhadap database dengan cepat. Berkat bantuan dari beberapa package, seperti Ardent, FactoryMuff dan Confide, Anda dapat meminimalkan jumlah kode pada model Anda, sementara menjaga tes bersih dan objektif.
Dalam sekuel untuk artikel ini, kami akan meninjau Controller pengujian. Menantikan!
Mari kita masih memulai dengan Laravel 4, mengajarkan Anda penting!