Bagaimana cara menghadapi sejumlah besar tes gagal? [Tutup]


22

Saya sedang mengerjakan pengembangan proyek lama yang ditulis di Jawa. Kami memiliki lebih dari 10 juta LOC dan, lebih buruk lagi, lebih dari 4000 tes fungsional.

Tes, dijadwalkan oleh Hudson, gagal seperti orang gila dengan setiap perubahan kode yang lebih besar. Verifikasi kegagalan pengujian - jika ini merupakan masalah pada produk atau dalam pengujian, membutuhkan waktu berbulan-bulan. Kami tidak dapat menghapus tes lama karena kami tidak tahu apa yang mereka uji!

Apa yang bisa kita lakukan Bagaimana cara melanjutkan tes legacy sebanyak itu?


6
Pertanyaan nyata punya jawaban. Daripada menjelaskan mengapa situasi Anda buruk, atau mengapa bos / rekan kerja Anda membuat Anda tidak bahagia, jelaskan apa yang ingin Anda lakukan untuk membuatnya lebih baik. Untuk informasi lebih lanjut, klik di sini ...
agas

13
Mengapa Anda membiarkan tes mulai gagal sejak awal? BTW 4000 tidak banyak tes untuk 10 MLOC
17овић

6
Berhenti, jatuhkan, dan gulung.
Navin

13
Cari tahu apa yang diuji. Kemudian kembalilah dan bertanya-tanya pertama-tama bagaimana pengujian di bumi memakan waktu berbulan-bulan untuk menemukan masalah, dan juga mencari tahu bagaimana persyaratan Anda berubah begitu banyak. Tes dimaksudkan untuk merangkum persyaratan dalam suatu aplikasi. Jika pengujian Anda gagal, kode Anda tidak berkinerja sesuai dengan persyaratan - baik Anda salah menuliskannya atau tidak satupun kode Anda mematuhi persyaratan Anda.
Dan Pantry

6
Kita semua telah melihat sebuah kompiler mengeluarkan banyak kesalahan karena ada satu yang hilang '}'. Jika ini adalah tes fungsional dengan sejumlah besar dependensi, mungkin jenis masalah yang sama sedang bekerja?
Dan Pichelman

Jawaban:


37

Tinggalkan mereka.

Saya tahu sulit untuk melepaskan sesuatu yang jelas-jelas merupakan upaya yang banyak untuk dilakukan, tetapi tes tidak bekerja untuk Anda, mereka bekerja melawan Anda. Test suite seharusnya memberi Anda kepercayaan diri bahwa sistem melakukan apa yang seharusnya dilakukan. Jika tidak melakukannya, itu merupakan kewajiban alih-alih aset. Tidak masalah apakah sistem atau pengujian salah - selama menjalankan test suite menandakan sejumlah besar kesalahan, itu tidak dapat memenuhi tujuannya.

Yang Anda butuhkan sekarang adalah serangkaian tes baru yang berjalan tanpa kesalahan. Itu berarti pada awalnya akan memiliki sedikit cakupan, bahkan hampir tidak ada cakupan. Setiap kali Anda memperbaiki atau meluangkan waktu untuk benar-benar memahami sesuatu tentang sistem Anda, Anda memesan kembali pengetahuan itu dalam ujian. Seiring waktu, ini akan menghasilkan jaring pengaman baru yang dapat Anda bangun di masa depan. Mencoba menambal jaring pengaman yang lama dan tidak dipahami adalah waktu yang hampir tidak pernah berharga.

Saya bahkan akan menganjurkan agar tidak mentransfer tes dari suite lama ke suite baru. Tentu, beberapa dari mereka mungkin berhasil sekarang, tetapi apakah itu karena mereka menguji dengan tepat apa yang seharusnya mereka uji, atau hanya karena beberapa tembakan acak selalu mengenai target? Jelas Anda harus pragmatis tentang apa yang bisa dan tidak bisa dilakukan dengan upaya yang Anda miliki untuk dibelanjakan, tetapi Anda tidak bisa berkompromi pada prinsip bahwa test suite harus berjalan dengan bersih untuk melakukan tugasnya .


9
Saya tidak bisa melihat logika dalam poin Anda: "Sebuah test suite seharusnya memberi Anda kepercayaan diri bahwa sistem melakukan apa yang seharusnya dilakukan. [...] Apa yang Anda butuhkan sekarang adalah serangkaian tes baru yang berjalan tanpa kesalahan. " Jika Anda memiliki kode yang salah yang membuat tes gagal tidak berarti Anda harus menulis ulang tes sehingga kode yang salah lolos.
DBedrenko

13
Situasi Hector adalah dia tidak tahu apakah kode atau tes itu salah . Jika ya, dia bisa bekerja dengan basis kode dan kadang-kadang mengubah tes, kadang-kadang kode bisnis. Seperti itu, bahkan pekerjaan seperti ini tidak akan membayar, karena Anda tidak tahu apakah Anda sedang memperbaiki masalah atau melakukannya.
Kilian Foth

5
"Test suite seharusnya memberi Anda kepercayaan diri bahwa sistem melakukan apa yang seharusnya." Tidak, itu seharusnya memberi tahu saya apakah sistem melakukan apa yang seharusnya; kepercayaan salah lebih buruk daripada tidak sama sekali. "Apa yang Anda butuhkan adalah rangkaian uji yang berjalan tanpa kesalahan" Tidak, yang ia butuhkan adalah rangkaian uji yang memberinya informasi berguna tentang kesehatan kode. Apa yang dia miliki sekarang adalah banyak lampu peringatan samar, yang lebih baik daripada lampu hijau dari test suite baru yang mengkilap yang tidak menguji apa pun. Dia harus menonaktifkan tes lama untuk sementara , tetapi tidak meninggalkan apa pun yang belum dia verifikasi palsu.
Beta

4
Jawaban ini adalah saran yang sangat buruk! Jika perubahan kode yang lebih kecil mengarah ke sejumlah besar tes gagal, Anda mungkin memiliki masalah kualitas kode. Tes setidaknya akan memberi tahu Anda bahwa Anda telah melanggar sesuatu. Anda perlu meningkatkan kode (dengan hati-hati refactoring dibantu oleh tes). Jika Anda hanya menghapus tes, Anda tidak memiliki cara untuk mengetahui apakah Anda melanggar sesuatu.
JacquesB

4
Ini saran yang mengerikan. Jika OP dan timnya sudah tidak dapat memahami basis kode dan itu tes, membuang tes dan memulai dari awal tidak mungkin untuk memecahkan masalah inti OP - memahami basis kode. Saya pikir kita dapat mengasumsikan tes bekerja ketika ditulis - jadi timnya perlu melacak apa yang diuji setiap tes, dan membaca sumber untuk menentukan apakah itu basis kode atau tes yang salah hari ini. Jauh lebih sederhana daripada memulai dari awal dengan tes yang tidak dipandu dan tidak diinformasikan / naif.
SnakeDoc

29

Pergi dan perbaiki tes.

Kesalahan terbesar Anda adalah bahwa Anda membiarkan tes gagal, dan Anda jelas mengabaikannya untuk sementara waktu. Apa yang Anda miliki bukan "tes warisan" - Anda sedang mengerjakan kode warisan. Dan saya menganggap setiap kode yang ditulis tanpa tes sebagai warisan.


Verifikasi kegagalan pengujian - jika masalah dalam produk atau dalam pengujian, membutuhkan waktu berbulan-bulan. Kami tidak dapat menghapus tes lama karena kami tidak tahu apa yang mereka uji!

Sepertinya ada masalah yang lebih besar di organisasi Anda, karena Anda tidak bekerja dengan persyaratan yang jelas. Saya tidak dapat mengerti bahwa Anda (atau orang lain) tidak dapat mengkonfirmasi perilaku yang benar.


4
Itu yang idealnya harus dilakukan, tetapi tampaknya tes di sini sangat buruk sehingga programmer bahkan tidak tahu apa yang mereka uji. Saya pikir dalam hal ini mungkin yang terbaik untuk menyingkirkan tes WTF dan mulai menulis yang baru dan bermakna segera! Dalam proyek baru-baru ini saya memiliki masalah yang sama dengan rekan kerja yang tesnya selalu gagal tanpa alasan yang baik (itu tidak gagal karena apa yang seharusnya diuji salah, tetapi karena kode tes begitu rapuh dan bahkan tidak deterministik!) . Saya menghabiskan berhari-hari menulis ulang apa yang saya bisa, dan menghancurkan sisanya!
Shautieh

@ Shautieh Tes WTF tidak berjalan tanpa kode WTF, jadi memperbaiki tes biasanya berarti refactoring kode. Dan tes yang gagal secara acak adalah tanda ketidakmampuan. Dan pengawas rekan kerja Anda yang harus disalahkan karena tidak melakukan pekerjaan mereka.
BЈовић

2
Terkadang hidup itu keras: orang yang bertanggung jawab atas tes WTF (dan kode) memperoleh gaji tertinggi dalam tim (20 +% lebih banyak dari saya), dan ketika dia berhenti di tengah proyek (karena dia menemukan pekerjaan dengan bayaran lebih tinggi) ) Saya harus mengambil beberapa devs-nya: / Tapi Anda benar benar untuk mengatakan atasan kami yang harus disalahkan juga ^^
Shautieh

@ Shautieh: seorang kolega saya pernah berkata bahwa bug dalam kode adalah dua bug: bug dalam kode dan titik buta dalam pengujian. Saya kira itu sebenarnya tiga jika Anda menghitung pengembang yang mentolerir tes gagal, dan empat jika Anda menghitung manajer yang mempromosikan tidak kompeten.
Beta

@ Beta Terdengar sangat mirip dengan definisi yang kadang-kadang digunakan dalam TDD: "Bug adalah tes yang belum Anda tulis."
Pasang kembali Monica

22

Tes sangat berharga. Paling tidak, mereka mencatat bahwa seseorang menganggap bahwa mereka harus menghabiskan waktu menulisnya, sehingga mungkin mereka memiliki nilai bagi seseorang sekali. Dengan sedikit keberuntungan, mereka akan berisi catatan lengkap dari semua fitur dan bug yang pernah dikerjakan tim - meskipun mereka mungkin juga merupakan cara untuk mencapai beberapa angka cakupan uji arbitrer tanpa dipikirkan dengan cermat. Sampai Anda melihat mereka, Anda tidak akan tahu yang terjadi di sini.

Jika sebagian besar tes Anda melewati sebagian besar waktu, maka cukup gigit peluru dan investasikan waktu untuk mencari tahu apa yang beberapa tes gagal coba lakukan, dan apakah memperbaiki atau memperbaikinya sehingga pekerjaan akan lebih mudah di waktu berikutnya. Dalam hal ini, lewati untuk Menentukan maksud untuk setiap bagian tes , untuk beberapa saran tentang apa yang harus dilakukan dengan sejumlah kecil tes gagal.

Di sisi lain, Anda mungkin dihadapkan dengan bangunan Merah sekarang, dan ratusan atau bahkan ribuan tes yang belum berlalu untuk sementara waktu, dan Jenkins belum menjadi Hijau untuk waktu yang lama. Pada titik ini, status build Jenkins menjadi tidak berguna, dan indikator utama masalah dengan check-in Anda tidak lagi berfungsi. Anda harus memperbaiki ini, tetapi tidak mampu menghentikan semua kemajuan saat Anda merapikan kekacauan di ruang tamu Anda.

Untuk menjaga kewarasan Anda saat melakukan arkeologi yang diperlukan untuk menentukan nilai apa yang dapat dipulihkan dari tes yang gagal, saya akan merekomendasikan langkah-langkah berikut:

Nonaktifkan sementara tes yang gagal.

Ada beberapa cara Anda bisa melakukan ini, tergantung pada lingkungan Anda, yang tidak Anda jelaskan sehingga saya tidak bisa merekomendasikan yang mana pun.

Beberapa kerangka kerja mendukung gagasan kegagalan yang diharapkan. Jika Anda melakukannya, maka ini bagus, karena Anda akan melihat hitungan mundur berapa banyak tes yang tersisa dalam kategori ini, dan Anda bahkan akan diberi tahu jika beberapa dari mereka mulai lulus secara tak terduga.

Beberapa kerangka kerja mendukung kelompok uji, dan memungkinkan Anda memberi tahu Hudson hanya untuk menjalankan beberapa tes, atau untuk melewatkan sekelompok tes. Ini berarti Anda sesekali dapat menjalankan kelompok uji secara manual untuk melihat apakah ada yang lewat sekarang.

Beberapa kerangka kerja memungkinkan Anda untuk membubuhi keterangan atau menandai tes tunggal untuk Diabaikan. Lebih sulit untuk menjalankan mereka sebagai kelompok dalam kasus ini, tetapi menghentikan mereka dari mengganggu Anda.

Anda mungkin memindahkan tes ke pohon sumber yang biasanya tidak termasuk dalam build.

Dalam keadaan ekstrim, Anda dapat menghapus kode dari HEAD sistem kontrol versi, tetapi ini akan membuat lebih sulit untuk mengenali kapan fase ketiga telah selesai.

Tujuannya adalah membuat Jenkins menjadi ramah lingkungan sesegera mungkin, sehingga Anda bisa mulai bergerak ke arah yang benar sesegera mungkin.

Pertahankan tes yang relevan.

Putuskan untuk menambahkan tes baru saat Anda menambah atau memodifikasi kode, dan berkomitmen untuk menjaga semua lulus tes lulus.

Tes mungkin gagal karena berbagai alasan, termasuk fakta bahwa mereka bukan tes yang ditulis dengan baik untuk memulai. Tapi begitu Anda mendapatkan Jenkins hijau, mempertahankannya seperti itu sangat penting.

Biasakan menulis tes yang bagus, dan buat itu menjadi masalah besar jika tes mulai gagal.

Tentukan niat untuk setiap tes.

Pergi melalui tes yang dinonaktifkan satu per satu. Mulailah dengan modul yang memengaruhi modul yang paling sering Anda ubah. Tentukan niat tes, dan alasan kegagalan.

  • Apakah ini menguji fitur yang dihapus dari basis kode dengan sengaja? Maka Anda mungkin dapat menghapusnya.

  • Apakah itu menangkap bug yang belum diketahui oleh siapa pun? Pasang kembali tes dan perbaiki bug.

  • Apakah gagal karena membuat asumsi yang tidak beralasan (mis. Asumsi teks tombol akan selalu dalam bahasa Inggris, tetapi sekarang Anda telah melokalkan aplikasi Anda untuk berbagai bahasa)? Kemudian cari tahu bagaimana membuat tes fokus pada satu hal, dan mengisolasinya dari perubahan yang tidak terkait sebaik mungkin.

  • Apakah tes terkapar di seluruh aplikasi, dan mewakili tes Sistem? Kemudian hapus dari suite uji Jenkins utama Anda dan tambahkan ke suite Regresi yang lebih jarang berjalan.

  • Apakah arsitektur aplikasi berubah tanpa bisa dikenali, sehingga tes tidak lagi berguna? Hapus.

  • Apakah tes ditambahkan ke peningkatan statistik cakupan kode secara buatan, tetapi sebenarnya tidak lebih dari memastikan bahwa kode tersebut dikompilasi dengan benar dan tidak masuk ke loop tak terbatas? Atau yang lain, tes hanya mengkonfirmasi bahwa kerangka kerja mengejek yang Anda pilih mengembalikan hasil yang baru saja Anda katakan? Hapus.

Sebagai hasil dari ini, beberapa tes akan berdiri, beberapa dimodifikasi, beberapa dibagi menjadi beberapa potongan independen, ukuran gigitan, dan beberapa dihapus. Selama Anda masih membuat kemajuan dengan persyaratan baru, sisihkan sedikit waktu untuk berurusan dengan utang teknis seperti ini adalah hal yang bertanggung jawab untuk dilakukan.


1
Gagasan yang benar-benar buruk untuk menonaktifkan tes hanya karena gagal! Saran Anda lainnya bagus, tapi bukan ini. Tes yang tidak Anda mengerti seharusnya tidak pernah dinonaktifkan. Titik pengujian bukan untuk mendapatkan bilah hijau, intinya adalah untuk mendapatkan perangkat lunak yang berfungsi!
JacquesB

Itu tergantung pada skala masalah. Tapi saya setuju, saya belum benar-benar menjelaskannya.
Bill Michell

Menambahkan paragraf untuk membedakan antara "kita hijau tetapi setiap perubahan membuat semuanya menjadi merah" dan "kita sudah merah begitu lama, kita sudah lupa seperti apa hijau itu"
Bill Michell

Alih-alih menonaktifkan atau bahkan menghapus tes, beberapa kerangka kerja juga memberikan gagasan tentang kegagalan yang diharapkan . Ini dapat membantu meningkatkan SNR karena Anda akan lebih langsung diberitahu tentang kegagalan baru (yang tidak akan Anda lakukan jika ada banyak kegagalan) tetapi masih akan diberitahu tentang kegagalan yang diketahui dan - bahkan mungkin lebih penting - ketika sebuah tes gagal sebelumnya tiba-tiba lulus lagi. Jika kegagalan tak terduga dibaca dan kegagalan yang diharapkan oranye, maka jadikan tes merah hijau sebagai yang pertama dan jadikan yang oranye hijau sebagai prioritas kedua.
5gon12eder

11

4000 tes adalah masalah yang sulit diatasi. 40 tes lebih mudah ditelusuri. Pilih secara acak sejumlah tes yang dapat dikelola untuk dijalankan dan dianalisis. Klasifikasi hasil sebagai:

  1. Tes tidak berguna
  2. Tes berguna yang berjalan bersih
  3. Tes berguna yang gagal

Jika banyak tes jatuh dalam kategori pertama, mungkin sudah saatnya untuk membuang suite tes Anda saat ini dan menyusun yang berguna untuk kode saat ini.

Jika banyak tes gagal dengan cara yang memberi tahu Anda tentang masalah dalam kode Anda, Anda perlu bekerja melalui tes gagal memperbaiki hal-hal. Anda mungkin menemukan bahwa memperbaiki satu atau dua bug membuat sejumlah besar tes berjalan.


2
+ (int) (PI / 3) untuk menyediakan cara yang sebenarnya & sederhana dari pengujian test suite - sementara saya setuju bahwa, sebagai aturan praktis, tes seperti yang dijelaskan oleh OP adalah tanda dari kesalahan desain - tapi tanpa pengujian apa yang salah, saran tentang test suite itu sendiri (baik itu "tinggalkan mereka", "perbaiki tes", "tulis tes baru") sama sekali tidak berguna. Persis seperti yang Anda katakan: jika saya memiliki tes 4k dan untuk 40 sepenuhnya acak dari 3/4 itu jelek dan tidak berguna - saya tidak akan bimbang untuk membuang seluruh rangkaian. Jika 3/4 dari mereka benar-benar bermanfaat - saya akan meninggalkannya & fokus pada peningkatan kode.
vaxquis

7

Jika pernyataan ini benar,

Tes ... gagal seperti gila dengan setiap perubahan kode yang lebih besar.

maka itu menyiratkan bahwa jika Anda mengembalikan kode sebelum "perubahan kode lebih besar", maka banyak tes akan berlalu lagi. Setelah melakukan itu, ambil sebagian kecil perubahan dan lihat tes mana yang baru gagal. Ini akan membantu Anda mengisolasi perubahan kode yang menyebabkan tes gagal. Untuk setiap tes, setelah Anda mengisolasi masalahnya, Anda harus dapat menentukan apakah kode baru itu cacat, atau tes itu. Jika ada masalah dengan kode baru, pastikan untuk membandingkannya dengan versi terbaru kalau-kalau bug tertentu telah diperbaiki.

Ulangi sampai Anda memiliki basis kode terbaru.

Ini mungkin tampak seperti tugas yang berat, tetapi sangat mungkin bahwa sekali Anda melewati jalan ini dan mulai mengisolasi beberapa masalah, sebuah pola akan mulai muncul yang mungkin sangat mempercepat prosesnya. Sebagai contoh:

  • Anda mungkin memperhatikan bahwa banyak tes tergantung pada hal lain yang cacat. Memperbaiki satu bagian itu dapat memperbaiki banyak tes.
  • Anda mungkin memperhatikan bahwa banyak tes yang cacat dan perlu diperbaiki atau dihapus.
  • Anda mungkin memperhatikan bahwa pengembang tertentu memiliki frekuensi yang jauh lebih tinggi untuk menyebabkan tes rusak. Pengembang itu mungkin membutuhkan lebih banyak pelatihan atau pengawasan.

3

Jika Anda tidak tahu apa yang mereka uji, hapus sampai Anda tahu. Pengujian adalah hal-hal yang lancar, jika Anda menghapus fitur yang tidak lagi diperlukan, maka Anda seharusnya harus mengubah tes yang menguji fitur itu! Jadi kecuali Anda tahu tes apa yang sedang diuji, Anda tidak memiliki harapan untuk mengubah basis kode dengan mereka di tempat.

Anda dapat mengatur sistem pengujian pada mesin pengembang dan menjalankannya di sana sehingga pengembang dapat melihat bagian mana dari tes yang berinteraksi, semoga memberikan dokumentasi yang hilang ini, dan menjadi lebih akrab dengan basis kode bahwa Anda tidak mengubah dengan benar, atau tidak pengujian lagi dengan benar.

Singkatnya - jika tes lama Anda gagal saat Anda membuat perubahan, perubahan kode Anda tidak baik. Gunakan tes-tes itu sebagai sarana pendidikan dalam cara kerja sistem.


1
Inilah mengapa saya suka @Ignoreanotasi JUnit - Anda dapat menyimpan tes Anda, tetapi tidak menjalankannya. Maka itu hanya masalah mengaktifkan kembali mereka dan memperbaikinya satu per satu. Ini memungkinkan Anda untuk mempersempit fokus Anda menjadi hanya beberapa tes pada satu waktu, bukannya kewalahan dengan ribuan kegagalan.
TMN

1
Ini saran yang buruk. Anda seharusnya tidak menghapus atau menonaktifkan tes yang tidak Anda mengerti. Hanya jika Anda benar- benar memahami tes ini, dan Anda yakin tes ini menguji fitur yang usang, seharusnya itu dinonaktifkan atau dihapus.
JacquesB

2

Hal terpenting yang akan saya lakukan adalah kembali ke dasar-dasar pengujian apa yang seharusnya dilakukan, dan apa yang dibutuhkan bisnis untuk terus bergerak. Tugas pengujian adalah mengidentifikasi masalah sebelum menjadi mahal untuk diperbaiki nanti. Saya pikir kata kunci dalam kalimat itu adalah "mahal." Masalah-masalah ini membutuhkan solusi bisnis. Apakah masalah mahal muncul di lapangan? Jika demikian, pengujian gagal total.

Manajemen Anda dan Anda perlu melakukan pemeriksaan realitas. Anda menemukan bahwa biaya pengembangan meroket karena serangkaian tes warisan. Bagaimana perbandingan biaya tersebut dengan biaya pengiriman produk yang salah karena Anda menonaktifkan tes? Bagaimana mereka dibandingkan dengan tugas berat untuk benar-benar mengetahui perilaku apa yang dibutuhkan pengguna (hal-hal apa yang harus diuji)?

Ini adalah masalah yang memerlukan solusi bisnis karena menyentuh sisi bisnis dari pekerjaan. Anda mengirimkan produk ke pelanggan, dan itu adalah batas yang sangat diminati bisnis. Mereka mungkin dapat mengidentifikasi solusi yang tidak bisa Anda, sebagai pengembang, lakukan. Sebagai contoh, mungkin masuk akal bagi mereka untuk menyediakan dua produk: satu produk "warisan" bagi mereka yang membutuhkan keandalan dan bersedia untuk melupakan fitur-fitur baru, dengan satu produk "visioner" yang mungkin memiliki lebih banyak kesalahan, tetapi sedang merintis lebih dulu. Ini akan memberi Anda kesempatan untuk mengembangkan dua set tes independen ... satu legacy satu dengan 4000 tes, dan satu dengan lebih banyak tes yang menurut Anda perlu dilakukan (dan dokumentasikan agar proses ini tidak berulang).

Kemudian, seni dimulai: bagaimana Anda bisa mengelola binatang berkepala dua ini sehingga kemajuan di satu cabang juga membantu cabang lainnya? Bagaimana pembaruan Anda ke cabang "visonary" mengalir kembali ke cabang "legacy", meskipun persyaratan pengujiannya kaku. Bagaimana cara melanjutkan permintaan pelanggan pada cabang "legacy" dengan lebih baik membentuk pemahaman Anda tentang persyaratan yang akan dibutuhkan pelanggan lama Anda jika Anda akhirnya menggabungkan kembali produk-produk tersebut?


-3

Kami tidak dapat menghapus tes lama karena kami tidak tahu apa yang mereka uji!

Itulah mengapa Anda harus menghapus tes lama! Jika Anda tidak tahu apa yang mereka lakukan, maka kegagalan tidak ada artinya dan menjalankannya adalah buang-buang waktu. Buang mereka dan mulai lagi.


2
ini sepertinya hanya mengulangi poin yang sudah dibuat dan dijelaskan dalam jawaban teratas
nyamuk

4
Kegagalan bukanlah "tidak berarti", itu berarti Anda tidak memahami sistem sebaik yang Anda kira.
Ben Voigt

Kegagalan jelas tidak ada artinya di sini, karena OP jelas menyatakan mereka tidak memahami sistem.
Mohair
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.