Saya baru dalam pengujian unit, dan saya terus menerus mendengar kata-kata 'benda tiruan' yang sering dilontarkan. Dalam istilah awam, dapatkah seseorang menjelaskan benda tiruan apa, dan apa yang biasanya mereka gunakan saat menulis tes unit?
Saya baru dalam pengujian unit, dan saya terus menerus mendengar kata-kata 'benda tiruan' yang sering dilontarkan. Dalam istilah awam, dapatkah seseorang menjelaskan benda tiruan apa, dan apa yang biasanya mereka gunakan saat menulis tes unit?
Jawaban:
Karena Anda mengatakan Anda baru dalam pengujian unit dan meminta objek tiruan dalam "istilah awam", saya akan mencoba contoh awam.
Bayangkan pengujian unit untuk sistem ini:
cook <- waiter <- customer
Secara umum mudah membayangkan pengujian komponen tingkat rendah seperti cook
:
cook <- test driver
Penguji hanya memesan hidangan yang berbeda dan memverifikasi juru masak mengembalikan hidangan yang benar untuk setiap pesanan.
Lebih sulit untuk menguji komponen tengah, seperti pelayan, yang memanfaatkan perilaku komponen lain. Penguji naif mungkin menguji komponen pelayan dengan cara yang sama seperti kami menguji komponen juru masak:
cook <- waiter <- test driver
Penguji akan memesan hidangan yang berbeda dan memastikan pelayan mengembalikan hidangan yang benar. Sayangnya, itu berarti bahwa pengujian komponen pelayan ini mungkin tergantung pada perilaku komponen koki yang benar. Ketergantungan ini bahkan lebih buruk jika komponen koki memiliki karakteristik uji-tidak ramah, seperti perilaku non-deterministik (menu termasuk kejutan koki sebagai hidangan), banyak ketergantungan (koki tidak akan memasak tanpa seluruh stafnya), atau banyak sumber daya (beberapa hidangan membutuhkan bahan-bahan mahal atau membutuhkan waktu satu jam untuk memasak).
Karena ini adalah tes pelayan, idealnya, kami ingin menguji hanya pelayan, bukan koki. Secara khusus, kami ingin memastikan pelayan menyampaikan pesanan pelanggan kepada juru masak dengan benar dan mengirimkan makanan juru masak kepada pelanggan dengan benar.
Pengujian unit berarti menguji unit secara independen, jadi pendekatan yang lebih baik adalah mengisolasi komponen yang diuji (pelayan) menggunakan apa yang disebut Fowler sebagai uji ganda (boneka, bertopik, palsu, pura-pura) .
-----------------------
| |
v |
test cook <- waiter <- test driver
Di sini, test cook adalah "bersekongkol" dengan driver tes. Idealnya, sistem yang diuji dirancang sehingga juru masak uji dapat dengan mudah diganti ( disuntikkan ) untuk bekerja dengan pelayan tanpa mengubah kode produksi (misalnya tanpa mengubah kode pelayan).
Sekarang, juru masak uji (tes ganda) dapat diimplementasikan dengan berbagai cara:
Lihat artikel Fowler untuk lebih spesifik tentang palsu vs bertopik vs tiruan vs boneka , tetapi untuk sekarang, mari kita fokus pada juru masak tiruan.
-----------------------
| |
v |
mock cook <- waiter <- test driver
Sebagian besar unit menguji komponen pelayan berfokus pada bagaimana pelayan berinteraksi dengan komponen koki. Pendekatan berbasis tiruan berfokus pada menentukan secara lengkap apa interaksi yang benar dan mendeteksi ketika berjalan serba salah.
Objek tiruan mengetahui terlebih dahulu apa yang seharusnya terjadi selama pengujian (mis. Panggilan metodenya mana yang akan dipanggil, dll.) Dan objek tiruan tahu bagaimana seharusnya bereaksi (misalnya apa nilai pengembalian yang diberikan). Mock akan menunjukkan apakah apa yang sebenarnya terjadi berbeda dari apa yang seharusnya terjadi. Objek tiruan kustom dapat dibuat dari awal untuk setiap kasus uji untuk mengeksekusi perilaku yang diharapkan untuk kasus uji tersebut, tetapi kerangka kerja mengejek berusaha untuk memungkinkan spesifikasi perilaku seperti itu secara jelas dan mudah ditunjukkan secara langsung dalam kasus uji.
Percakapan seputar tes berbasis mock mungkin terlihat seperti ini:
driver tes untuk mengejek juru masak : mengharapkan pesanan hot dog dan memberinya hot dog dummy ini sebagai tanggapan
driver tes (berpose sebagai pelanggan) kepada pelayan : Saya ingin hot dog, silakan
pelayan untuk mengejek juru masak : 1 hot dog, silakan
mengejek koki untuk pelayan : pesan: 1 hot dog siap (memberikan hot dog dummy untuk pelayan)
pelayan untuk menguji pengemudi : inilah hot dog Anda (memberikan hot dog dummy untuk menguji pengemudi)test driver : UJI BERHASIL!
Tetapi karena pelayan kami baru, inilah yang bisa terjadi:
driver tes untuk mengejek juru masak : mengharapkan pesanan hot dog dan memberinya hot dog dummy ini sebagai tanggapan
pengemudi tes (berpose sebagai pelanggan) kepada pelayan : Saya ingin hot dog, silakan
pelayan untuk mengejek juru masak : 1 hamburger tolong
tiruan juru masak menghentikan tes: Saya diberitahu untuk mengharapkan pesanan hot dog!pengemudi ujian mencatat masalahnya: UJI GAGAL! - pelayan mengubah urutan
atau
driver tes untuk mengejek juru masak : mengharapkan pesanan hot dog dan memberinya hot dog dummy ini sebagai tanggapan
driver tes (berpose sebagai pelanggan) kepada pelayan : Saya ingin hot dog, silakan
pelayan untuk mengejek juru masak : 1 hot dog, silakan
mengejek koki untuk pelayan : pesan: 1 hot dog siap (memberikan hot dog dummy untuk pelayan)
pelayan untuk menguji pengemudi : di sini adalah kentang goreng Anda (memberikan kentang goreng dari beberapa pesanan lain untuk menguji driver)test driver mencatat kentang goreng yang tak terduga: UJI GAGAL! pelayan mengembalikan hidangan yang salah
Mungkin sulit untuk secara jelas melihat perbedaan antara benda tiruan dan bertopik tanpa contoh berbasis rintisan yang kontras untuk pergi dengan ini, tetapi jawaban ini sudah terlalu lama :-)
Juga perhatikan bahwa ini adalah contoh yang cukup sederhana dan bahwa kerangka kerja mengejek memungkinkan untuk beberapa spesifikasi yang cukup canggih dari perilaku yang diharapkan dari komponen untuk mendukung tes komprehensif. Ada banyak materi tentang objek tiruan dan kerangka kerja mengejek untuk informasi lebih lanjut.
Objek Mock adalah objek yang menggantikan objek nyata. Dalam pemrograman berorientasi objek, objek tiruan adalah objek simulasi yang meniru perilaku objek nyata dengan cara yang terkontrol.
Seorang programmer komputer biasanya membuat objek tiruan untuk menguji perilaku beberapa objek lain, dengan cara yang sama seperti seorang perancang mobil menggunakan tiruan uji tabrakan untuk mensimulasikan perilaku dinamis manusia dalam dampak kendaraan.
http://en.wikipedia.org/wiki/Mock_object
Objek tiruan memungkinkan Anda mengatur skenario pengujian tanpa membawa sumber daya besar dan sulit seperti database. Alih-alih memanggil basis data untuk pengujian, Anda dapat mensimulasikan basis data Anda menggunakan objek tiruan dalam pengujian unit Anda. Ini membebaskan Anda dari beban harus mengatur dan meruntuhkan database nyata, hanya untuk menguji satu metode di kelas Anda.
Kata "Mock" kadang-kadang keliru digunakan secara bergantian dengan "Stub." Perbedaan antara kedua kata tersebut dijelaskan di sini. Pada dasarnya, tiruan adalah objek rintisan yang juga mencakup harapan (yaitu "pernyataan") untuk perilaku yang tepat dari objek / metode yang diuji.
Sebagai contoh:
class OrderInteractionTester...
public void testOrderSendsMailIfUnfilled() {
Order order = new Order(TALISKER, 51);
Mock warehouse = mock(Warehouse.class);
Mock mailer = mock(MailService.class);
order.setMailer((MailService) mailer.proxy());
mailer.expects(once()).method("send");
warehouse.expects(once()).method("hasInventory")
.withAnyArguments()
.will(returnValue(false));
order.fill((Warehouse) warehouse.proxy());
}
}
Perhatikan bahwa objek warehouse
dan mailer
tiruan diprogram dengan hasil yang diharapkan.
Benda tiruan adalah benda tiruan yang meniru perilaku benda nyata. Biasanya Anda menulis objek tiruan jika:
Objek Mock adalah salah satu jenis Tes Ganda . Anda menggunakan mockobjects untuk menguji dan memverifikasi protokol / interaksi kelas yang diuji dengan kelas lain.
Biasanya Anda akan jenis 'program' atau 'catatan' harapan: metode panggilan yang Anda harapkan kelas Anda lakukan ke objek yang mendasarinya.
Katakanlah misalnya kita sedang menguji metode layanan untuk memperbarui bidang dalam Widget. Dan itu dalam arsitektur Anda ada WidgetDAO yang berkaitan dengan database. Berbicara dengan database lambat dan pengaturannya dan pembersihan setelah itu rumit, jadi kami akan mengejek WidgetDao.
mari kita pikirkan apa yang harus dilakukan oleh layanan: itu harus mendapatkan Widget dari database, melakukan sesuatu dengannya dan menyimpannya lagi.
Jadi dalam bahasa pseudo dengan pseudo-mock library kita akan memiliki sesuatu seperti:
Widget sampleWidget = new Widget();
WidgetDao mock = createMock(WidgetDao.class);
WidgetService svc = new WidgetService(mock);
// record expected calls on the dao
expect(mock.getById(id)).andReturn(sampleWidget);
expect(mock.save(sampleWidget);
// turn the dao in replay mode
replay(mock);
svc.updateWidgetPrice(id,newPrice);
verify(mock); // verify the expected calls were made
assertEquals(newPrice,sampleWidget.getPrice());
Dengan cara ini kita dapat dengan mudah menguji pengembangan drive dari kelas yang bergantung pada kelas lain.
Saya sangat merekomendasikan artikel hebat dari Martin Fowler yang menjelaskan apa sebenarnya ejekan itu dan bagaimana perbedaannya dengan bertopik.
Saat unit menguji beberapa bagian dari program komputer, Anda idealnya hanya ingin menguji perilaku bagian tertentu itu.
Sebagai contoh, lihat pseudo-code di bawah ini dari bagian imajiner dari program yang menggunakan program lain untuk memanggil print sesuatu:
If theUserIsFred then
Call Printer(HelloFred)
Else
Call Printer(YouAreNotFred)
End
Jika Anda menguji ini, Anda terutama ingin menguji bagian yang melihat apakah pengguna adalah Fred atau tidak. Anda tidak benar-benar ingin menguji Printer
bagian dari semuanya. Itu akan menjadi ujian lain.
Ini adalah di mana benda Mock datang. Mereka berpura-pura menjadi jenis lain hal. Dalam hal ini Anda akan menggunakan Mock Printer
sehingga akan bertindak seperti printer sungguhan, tetapi tidak akan melakukan hal-hal yang tidak nyaman seperti mencetak.
Ada beberapa jenis objek pura-pura yang bisa Anda gunakan yang bukan Mock. Hal utama yang membuat Mocks Mocks adalah bahwa mereka dapat dikonfigurasi dengan perilaku dan harapan.
Ekspektasi memungkinkan Mock Anda meningkatkan kesalahan saat digunakan secara tidak benar. Jadi, dalam contoh di atas, Anda dapat memastikan bahwa Printer dipanggil dengan HelloFred di dalam case test "user is Fred". Jika itu tidak terjadi, Mock Anda dapat memperingatkan Anda.
Perilaku di Mocks berarti bahwa, misalnya, kode Anda melakukan sesuatu seperti:
If Call Printer(HelloFred) Returned SaidHello Then
Do Something
End
Sekarang Anda ingin menguji apa yang kode Anda lakukan ketika Printer dipanggil dan mengembalikan SaidHello, sehingga Anda dapat mengatur Mock untuk mengembalikan SaidHello ketika dipanggil dengan HelloFred.
Salah satu sumber yang bagus untuk hal ini adalah Martin Fowlers memposting Mocks Aron't Stubs
Objek tiruan dan rintisan adalah bagian penting dari pengujian unit. Bahkan mereka jauh untuk memastikan Anda menguji unit , bukan kelompok unit.
Singkatnya, Anda menggunakan bertopik untuk mematahkan ketergantungan (System Under Test) SUT pada objek dan tiruan lain untuk melakukan itu dan memverifikasi bahwa SUT disebut metode / properti tertentu pada dependensi. Ini kembali ke prinsip dasar pengujian unit - bahwa pengujian harus mudah dibaca, cepat dan tidak memerlukan konfigurasi, yang mungkin menyiratkan semua kelas nyata.
Secara umum, Anda dapat memiliki lebih dari satu rintisan dalam ujian Anda, tetapi Anda seharusnya hanya memiliki satu tiruan. Ini karena tujuan mock adalah untuk memverifikasi perilaku dan pengujian Anda seharusnya hanya menguji satu hal.
Skenario sederhana menggunakan C # dan Moq:
public interface IInput {
object Read();
}
public interface IOutput {
void Write(object data);
}
class SUT {
IInput input;
IOutput output;
public SUT (IInput input, IOutput output) {
this.input = input;
this.output = output;
}
void ReadAndWrite() {
var data = input.Read();
output.Write(data);
}
}
[TestMethod]
public void ReadAndWriteShouldWriteSameObjectAsRead() {
//we want to verify that SUT writes to the output interface
//input is a stub, since we don't record any expectations
Mock<IInput> input = new Mock<IInput>();
//output is a mock, because we want to verify some behavior on it.
Mock<IOutput> output = new Mock<IOutput>();
var data = new object();
input.Setup(i=>i.Read()).Returns(data);
var sut = new SUT(input.Object, output.Object);
//calling verify on a mock object makes the object a mock, with respect to method being verified.
output.Verify(o=>o.Write(data));
}
Dalam contoh di atas saya menggunakan Moq untuk menunjukkan stub dan ejekan. Moq menggunakan kelas yang sama untuk keduanya - Mock<T>
yang membuatnya sedikit membingungkan. Apapun, saat runtime, tes akan gagal jika output.Write
tidak dipanggil dengan data parameter
, sedangkan kegagalan untuk menelepon input.Read()
tidak akan gagal itu.
Seperti jawaban lain yang disarankan melalui tautan ke " Mocks Aron't Stubs ", mock adalah bentuk "test double" untuk digunakan sebagai pengganti objek nyata. Apa yang membuatnya berbeda dari bentuk tes ganda lainnya, seperti objek rintisan, adalah bahwa tes ganda lainnya menawarkan verifikasi keadaan (dan simulasi opsional) sedangkan tiruan menawarkan verifikasi perilaku (dan simulasi opsional).
Dengan sebuah rintisan, Anda dapat memanggil beberapa metode pada rintisan dalam urutan apa pun (atau bahkan secara repitious) dan menentukan keberhasilan jika rintisan tersebut telah mengambil nilai atau keadaan yang Anda maksudkan. Sebaliknya, objek tiruan mengharapkan fungsi yang sangat spesifik untuk dipanggil, dalam urutan tertentu, dan bahkan beberapa kali tertentu. Tes dengan objek tiruan akan dianggap "gagal" hanya karena metode dipanggil dalam urutan atau hitungan yang berbeda - bahkan jika objek tiruan memiliki keadaan yang benar ketika tes berakhir!
Dengan cara ini, objek tiruan sering dianggap lebih erat digabungkan ke kode SUT daripada objek rintisan. Itu bisa menjadi hal yang baik atau buruk, tergantung pada apa yang Anda coba verifikasi.
Bagian dari titik menggunakan objek tiruan adalah bahwa mereka tidak harus benar-benar diimplementasikan sesuai dengan spesifikasi. Mereka hanya bisa memberikan tanggapan bodoh. Misalnya jika Anda harus mengimplementasikan komponen A dan B, dan keduanya "memanggil" (berinteraksi satu sama lain), maka Anda tidak dapat menguji A sampai B diimplementasikan, dan sebaliknya. Dalam pengembangan uji coba, ini adalah masalah. Jadi Anda membuat mock ( "bodoh") obyek untuk A dan B, yang sangat sederhana, tetapi mereka memberikan beberapa jenis respon ketika mereka berinteraksi dengan. Dengan begitu, Anda bisa menerapkan dan menguji A menggunakan objek tiruan untuk B.
Untuk php dan phpunit dijelaskan dengan baik dalam phpunit documentaion. Lihat disini dokumentasi phpunit
Dalam kata sederhana objek mengejek hanyalah objek tiruan dari objek asli Anda dan mengembalikan nilai kembalinya, nilai pengembalian ini dapat digunakan di kelas uji
Ini adalah salah satu perspektif utama dari tes unit. ya, Anda mencoba menguji unit kode tunggal Anda dan hasil pengujian Anda seharusnya tidak relevan dengan perilaku kacang atau objek lainnya. jadi Anda harus mengejeknya dengan menggunakan benda-benda Mock dengan beberapa respons yang disederhanakan.