Skip to main content

Soal Test Class di Salesforce.com

Sumber gambar: force.com
Aku adalah penganut falsafah "learning by doing", artinya lebih suka untuk praktek langsung daripada repot baca teori. Ini berlaku juga saat membeli barang baru, langsung utak-atik tanpa mau repot-repot membaca buku petunjuk penggunaan. Termasuk saat belajar pengembangan aplikasi di Salesforce.com (force.com). Seminggu pertama baca-baca dan mengikuti tutorial sangatlah membosankan, dan baru semangat ketika terjun langsung dalam proyek, dan belajar dengan mencontek hasil kerjaan orang lain. Akibatnya, tidak memiliki dasar yang mendalam.

Termasuk dalam hal membuat testclass. Selama ini aku anggap testclass hanya sebagai basa-basi, formalitas agar aplikasi bisa di-deploy ke production. Soalnya system mewajibkan minimal code coverage 75%, baru bisa di-deploy. Tentu saja ini hal yang menjengkelkan dan membuat frustrasi. Hal ini diperparah dengan kebiasaanku yang tidak pernah membaca teori tentang test class ataupun cara yang benar untuk membuat test class. Jadinya asal jadi saja, yang penting bisa di-deploy.

Masalah muncul ketika kerumitan memperoleh code coverage dikombinasikan dengan adanya berbagai governor limit, terutama SOQL limit. Secara standard, dalam satu alur eksekusi, salesforce membatasi hanya boleh ada 100 kali query untuk mengambil data (SELECT). Ini termasuk query yang terjadi dalam trigger, yang mungkin memicu rangkaian trigger dari object lain yang terpengaruh. Oke lah, dalah prakteknya aku bisa paham, karena sifatnya multi-tenant, jadi masing-masing tidak boleh mendominasi resource. Tapi ini kan cuma test class.

Kejengkelanku bertambah karena ternyata di dalam test class, penerapan governor limit itu merupakan akumulasi dari seluruh proses awal. Jadi misalnya aku ada melakukan insert record untuk 3 object berbeda, selama itu masih dalam satu fungsi testclass, akan dianggap sebagai satu eksekusi proses dan akumulasi penggunaan resource akan dikontrol dengan governor limit. Padahal dalam prakteknya, sangat mustahil proses itu berjalan sekaligus. Biasanya user akan insert record A, kemudian pindah menu untuk insert record B, dan terakhir pindah menu untuk insert record C. Jadi dalam prakteknya akan ada 3 eksekusi, yang masing-masing memiliki penggunaan resource yang terpisah. Payahnya, seringkali aku harus membuat testclass untuk object X, dimana untuk bisa membuat record object X itu perlu terlebih dahulu dibuat record untuk object A, B, C, D dst. Pokoknya prosesnya panjang. Kadang baru sampai di pembuatan record D saja sudah mentok kena governor limit.

Salah satu cara untuk mengakali adalah dengan memanfaatkan @isTest(SeeAllData=true)   supaya bisa memanfaatkan data yang sudah ada untuk membuat test class bagi trigger/class yang aku buat. Keuntungannya adalah aku tidak perlu melewati tahap insert record  A, B, C karena asumsi record sudah ada. Kekurangannya jelas kita harus memastikan bahwa data untuk dipakai itu sudah ada, kalau gak ada ya test classnya jadi mubazir, dan ujung-ujungnya bisa gagal mencapai code coverage 75%. Sejauh ini teknik ini banyak membantu. Teknik lain adalah dengan memecah-mecah test class menjadi beberapa fungsi testclass, karena setiap fungsi akan di-reset perhitungan governor limitnya. Ini juga cukup membantu, asalkan alur prosesnya tidak terlalu panjang.

Tapi hari ini aku kembali mentok, gara-gara kena governor limit. Padahal sudah pakai SeeAllData, dan prosesnya pun tergolong pendek. Entah kenapa tetap saja kena query limit. Iseng-iseng aku nyeletuk di twitter, eh kok ada yang membalas, katanya soal test class itu cuma masalah pengalaman, kalau sudah terbiasa akan menjadi "piece of cake". Busyet .... pengen lempar asbak rasanya. Untung aku gak ngerokok :D

Setelah hampir seharian ngoprek, sampai lebih dari 10 kali mencoba melakukan deployment dan masih gagal, aku coba cari solusi di internet. Di situlah aku nemu tentang fungsi startTest() dan stopTest(). Lebih lengkapnya Test.startTest() dan Test.stopTest(). Rupanya aku bisa menggunakan ini untuk menentukan bagian mana yang aku anggap untuk testing, dan saat mulai memanggil startTest itu, perhitungan governor limit akan di-reset. Jadi aku bisa mengumpulkan data dulu, bikin record A, B, C misalnya, baru waktu mau mulai bikin record X, aku panggil startTest(). Dan hasilnya .... mujarab! Masalah governor limit teratasi. Jadi sekarang aku punya 3 senjata :

  1. Menggunakan SeeAllData=true
  2. Memecah menjadi beberapa fungsi testclass
  3. Menggunakan StartTest dan StopTest


Oh ya, sepertinya aku harus lebih banyak baca teori. Jadi mendingan aku salin saja "Testing Best Practice" sebagai pengingat (dengan sedikit improvisasi, bukan sekedar terjemahan)

  1. Capailah coverage sebanyak mungkin, untuk setiap kelas. Jadi jangan hanya mengandalnya total rata-rata, yang minimal 75%
  2. Jika ada logika kondisi (if, case dsb), usahakan untuk membuat test class untuk setiap kondisi
  3. Lakukan pemanggilan fungsi dengan inputan data yang benar maupun yang tidak benar
  4. Pastikan testclass tidak menghasilkan error, kecuali kalau errror itu bisa dijebak dengan benar (tidak menimbulkan fatal error)
  5. Pastikan menangani setiap peluang kesalahan (exception) dengan benar, tidak hanya menjebaknya.
  6. Gunakan metode System.assert untuk memastikan kode program berperilaku dengan benar
  7. Gunakan metode RunAs untuk melakukan pengujian dengan berbagai profil yang berbeda
  8. Latih dengan menggunakan data yg banyak, minimal 20 record dalam testing yang dibuat
  9. Gunakan ORDER BY untuk memastikan hasil yang diinginkan dalam urutan yang sesuai
  10. Jangan berasumsi bahwa Record ID itu pasti berurutan
  11. Kumpulkan data yang diperlukan sebelum membuat kode untuk testing, dan setelah itu baru panggil fungsi Test.startTest().
  12. Buat komentar selengkap mungkin, tidak hanya tujuan pengujian, melainkan juga data awal dan data akhir yang diharapkan, perkecualian dan lain-lain.
  13. Buat test class untuk masing-masing kelas secara terpisah, jangan hanya membuat satu test class untuk seluruh aplikasi.
Setidaknya dalam 3 tahun pengalaman ngulik force.com ini, sudah 50% dari saran-saran diatas yang aku ikuti hehehe..... perlu lebih disiplin lagi nih :)

Update:
Ada satu fungi yang belakangan aku temukan adalah Test.isRunningTest(), untuk mengetahui apakah yang menjalankan code adalah program test atau bukan. Jadi ada baiknya, di class/trigger yang menggunakan penggalan code yang tidak bisa ditest, seperti callout, diabaikan saat test.

Comments

Popular posts from this blog

PostGreSQL :: Hitung Umur

Ternyata untuk menghitung umur dari data yang disimpan di PostGreSQL sangat gampang. PostGreSQL sendiri sudah menyediakan fungsi yang mendukung. Beberapa fungsi yang bisa dipakai adalah AGE dan EXTRACT. AGE dipakai untuk menghitung umur dari sebuah data, dibandingkan dengan hari ini, atau dengan data lain (tipenya timestamp). Misal: age(timestamp '1980-09-27'), akan menghasilkan nilai (bertipe interval) "24 years 1 mon 25 days". Nah, kalau mau mengambil nilai tahunnya saja, tinggal menggunakan EXTRACT Contoh: EXTRACT(year FROM AGE(timestamp '2001-09-27')), hasilnya akan jadi 24.

PHP :: Selisih Jam

Setelah kemarin kesulitan untuk melakukan increment ataupun decrement terhadap variabel waktu (date/time) di PHP, kali ini nemuin masalah untuk mencari selisih waktu (dari satu jam ke jam tertentu), yang perlu untuk ngitung lembur karyawan. Tadinya kepikiran untuk buat fungsi sendiri, yang flow -nya kira-kira begini: - masing-masing dipisah menjadi jam, menit, detik - bandingkan antara keduanya. - lakukan pengurangan terhadap masing-masing komponen (jam, menit dan detik) - gabungkan hasil perhitungan ... (selisih jam + selisih menit + selisih detik) Tapi waktu aku ingat kasus Next Date, ... muncul ide untuk menggunakan cara yang sama, yaitu memanfaatkan format UNIX timestamp, terus nyoba mencari selisihnya. TERNYATA BERHASIL !!! Flownya seperti ini: - masing-masing dipisah menjadi jam, menit,detik - ubah masing-masing ke format timestamp, gunakan fungsi mktime() - kurangkan kedua jam - hasilnya dibagi 60 (karena satuannya pakai menit), sementara selisih timestamp itu dalam detik Algori

Delphi :: Split String

Akhirnya ketemu juga cara untuk melakukan split string. (Terbiasa pakai PHP sih, yang sangat memanjakan dalam pengelolaan string.) Di sini memanfaatkan TStringList, unit Classes. // procedure untuk split string procedure Split (const Delimiter: Char; // delimiter charachter Input: string; // input string const Strings: TStrings) ; // list of string result begin Assert(Assigned(Strings)) ; Strings.Clear; Strings.Delimiter := Delimiter; Strings.DelimitedText := Input; end; // contoh pemakaian procedure TForm1.Button1Click(Sender: TObject) ; var A: TStringList; begin A := TStringList.Create; try Split(' ', 'your delphi guide', A) ; ShowMessage(a[0]) ; //your ShowMessage(a[1]) ; //delphi ShowMessage(a[2]) ; //guide finally A.Free; end; end; Source : http://delphi.about.com/cs/adptips2002/a/bltip1102_5.htm Wheew .. akhirnya. One step ahead!!