Unit Testing – Bagian II: Bagaimana
Sebuah konsep tanpa implementasi atau sebuah teori tanpa praktek ibarat janji tinggal janji (tak ditepati). Konsep unit testing sudah saya bahas pada artikel sebelumnya, Unit Testing - Bagian I: Apa, Mengapa. Bagaimana implementasi unit testing pada Software Engineering? Kali ini, saya akan coba untuk membahasnya dengan menggunakan teknologi Microsoft. Sebelum membaca lebih lanjut, bagi anda yang belum mengerti apa itu unit testing, mungkin bisa baca konsep nya terlebih dahulu. Karena keterbalikan dari janji tinggal janji, sebuah implementasi tanpa konsep ibarat cinta buta, apa yang dilakukan tak lebih dari luapan hati dan perasaan... halaaa jadi ngaco deh..ahahaa. Lupakan tentang cinta, saya juga bukan ahlinya ;P
Saya bikin sebuah class bernama Fighter pada project Fighting.Api di dalam sebuah solution bernama Fighting, dengan beberapa member (method dan field) seperti berikut:
public class Fighter
{
#region Constants
private readonly double POWER_FACTOR = 0.5;
private readonly double SPECIAL_KICK_POWER = 3.0;
private readonly double SPECIAL_PUNCH_POWER = 2.5;
private readonly double FIRST_COMBO_POWER = 5.0;
private readonly double SECOND_COMBO_POWER = 7.5;
private readonly double ORDINARY_MOVE_POWER = 2.0;
#endregion
#region Private Fields
private int _opponentDistance;
#endregion
#region Properties
/// <summary>
/// Gets or sets the opponent distance.
/// </summary>
/// <value>The opponent distance.</value>
public int OpponentDistance
{
get
{
if( _opponentDistance < 0 )
{
return 0;
}
return _opponentDistance;
}
set
{
_opponentDistance = value;
}
}
#endregion
#region Methods
/// <summary>
/// Attacks enemy using a specific attack type.
/// </summary>
/// <param name="attackType">Type of the attack.</param>
/// <returns></returns>
public double Attack( AttackType attackType )
{
switch( attackType )
{
case AttackType.SpecialKick:
return SPECIAL_KICK_POWER * POWER_FACTOR;
case AttackType.SpecialPunch:
return SPECIAL_PUNCH_POWER * POWER_FACTOR;
case AttackType.Combo1:
return FIRST_COMBO_POWER * POWER_FACTOR;
case AttackType.Combo2:
return SECOND_COMBO_POWER * POWER_FACTOR;
default:
// An ordinary weak punch or kick.
return ORDINARY_MOVE_POWER * POWER_FACTOR;
}
}
/// <summary>
/// Moves the fighter to the specified direction by a specified step.
/// </summary>
/// <param name="moveDirection">The move direction.</param>
/// <param name="step">The step.</param>
public void Move( MoveDirection moveDirection, int step )
{
if( step <= 0 )
{
return;
}
switch( moveDirection )
{
case MoveDirection.Forward:
_opponentDistance -= step;
break;
case MoveDirection.Back:
_opponentDistance += step;
break;
default:
// don't move, do nothing.
break;
}
}
#endregion
}
Visual Studio telah menyediakan kemudahan dalam melakukan unit testing. Framework yang digunakan termasuk xUnit framework, yang terdiri dari text fixtures, test suites, test execution, dan assertion. Lebih lengkap tentang xUnit, bisa ditelusuri di Google, Wikipedia, dan referensi lainnya.
First Things First
Saya menggunakan Visual Studio Team System 2008 dengan .NET Framework versi 3.5 (dan bahasa C# tentunya ;D). Pertama, kita memerlukan sebuah test project sebelum menulis, mengeksekusi, dan menganalisa hasil unit test.
Cara yang paling gampang, dari dalam class yang akan diuji, klik kanan --> Create Unit Test. Akan muncul sebuah window Create Unit Tests.
Di situ terlihat class Fighter beserta membernya. Cukup kita centang member mana saja yang akan kita uji. Kita hilangkan centang pada constructor Fighter(), karena tidak ada deklarasi constructor secara eksplisit pada class Fighter, sehingga tidak ada code yang bisa diuji. Pada bagian Output project, terlihat pilihan Create a new C# test project. Ketika kita klik OK, maka akan muncul kotak dialog untuk memasukkan nama test project yang baru.
Terakhir, klik tombol pamungkas kita, yaitu Create.
What next?
Maka Visual Studio akan membuatkan sebuah class baru bernama FighterTest dengan bentuk awal seperti ini:
using Fighting.Api;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Fighting.Api.Test
{
/// <summary>
///This is a test class for FighterTest and is intended
///to contain all FighterTest Unit Tests
///</summary>
[TestClass()]
public class FighterTest
{
private TestContext testContextInstance;
/// <summary>
///Gets or sets the test context which provides
///information about and functionality for the current test run.
///</summary>
public TestContext TestContext
{
get
{
return testContextInstance;
}
set
{
testContextInstance = value;
}
}
#region Additional test attributes
//
//You can use the following additional attributes as you write your tests:
//
//Use classInitialize to run code before running the first test in the class
//[ClassInitialize()]
//public static void MyClassInitialize(TestContext testContext)
//{
//}
//
//Use classCleanup to run code after all tests in a class have run
//[ClassCleanup()]
//public static void MyClassCleanup()
//{
//}
//
//Use TestInitialize to run code before running each test
//[TestInitialize()]
//public void MyTestInitialize()
//{
//}
//
//Use TestCleanup to run code after each test has run
//[TestCleanup()]
//public void MyTestCleanup()
//{
//}
//
#endregion
/// <summary>
///A test for OpponentDistance
///</summary>
[TestMethod()]
public void OpponentDistanceTest()
{
Fighter target = new Fighter(); // TODO: Initialize to an appropriate value
int expected = 0; // TODO: Initialize to an appropriate value
int actual;
target.OpponentDistance = expected;
actual = target.OpponentDistance;
Assert.AreEqual( expected, actual );
Assert.Inconclusive( "Verify the correctness of this test method." );
}
/// <summary>
///A test for Move
///</summary>
[TestMethod()]
public void MoveTest()
{
Fighter target = new Fighter(); // TODO: Initialize to an appropriate value
MoveDirection moveDirection = new MoveDirection(); // TODO: Initialize to an appropriate value
int step = 0; // TODO: Initialize to an appropriate value
target.Move( moveDirection, step );
Assert.Inconclusive( "A method that does not return a value cannot be verified." );
}
/// <summary>
///A test for Attack
///</summary>
[TestMethod()]
public void AttackTest()
{
Fighter target = new Fighter(); // TODO: Initialize to an appropriate value
AttackType attackType = new AttackType(); // TODO: Initialize to an appropriate value
double expected = 0F; // TODO: Initialize to an appropriate value
double actual;
actual = target.Attack( attackType );
Assert.AreEqual( expected, actual );
Assert.Inconclusive( "Verify the correctness of this test method." );
}
}
}
Wuidiih!! Ini class baru jadi, tapi isinya udah aneh banget, lumayan panjang pulak!! Tenang.. nggak perlu cemas.. nanti, klo udah terbiasa unit testing, bakal ngerasa klo ini adalah hal yang simple kok : )
Jika kita perhatikan, terdapat tiga buah method di dalam class ini yaitu OpponentDistanceTest, MoveTest, dan AttackTest. Ketiga method ini lah muara dari semua teori yang saya bahas pada Bagian I. Ini lah titik terang unit testing itu. Di dalam ketiga method ini, kita akan membuat unit test terhadap class Fighter. Sebelumnya, kita bisa melihat attribute TestMethod di atas ketiga method ini. Attribute TestMethod diperlukan oleh framework unit testing Visual Studio (namespace Microsoft.VisualStudio.TestTools.UnitTesting) untuk menandakan dan memperlakukan method tersebut sebagai sebuah method untuk unit test. Reflection adalah aktor utama di balik semua proses eksekusi test method di dalam framework unit testing Visual Studio. Selain itu, terdapat pula attribute TestClass pada bagian atas deklarasi class. Tapi saya tidak akan membahas tentang attribute dan reflection di sini, mungkin reflection akan saya bahas di artikel yang lain (rasaan pernah janji sebelumnya ;D).
Kembali ke class FighterTest. Method AttackTest adalah sebuah test method yang akan melakukan pengujian terhadap method Attack, dan begitu pula masing-masing dari dua test method lainnya. Baiklah, mari kita lakukan unit test yang pertama! Perhatikan method AttackTest, di dalamnya ada dua local variable bernama expected dan actual yang sama-sama bertipe double. Variable actual akan digunakan untuk menampung nilai output aktual dari target yang diuji (method Attack pada class Fighter). Sementara, variable expected akan kita gunakan untuk mendefinisikan nilai ekspektasi output yang kita inginkan. Ngintip method Attack di class Fighter lagi yuuk.. yuu marii..
public double Attack( AttackType attackType )
{
switch( attackType )
{
case AttackType.SpecialKick:
return SPECIAL_KICK_POWER * POWER_FACTOR;
case AttackType.SpecialPunch:
return SPECIAL_PUNCH_POWER * POWER_FACTOR;
case AttackType.Combo1:
return FIRST_COMBO_POWER * POWER_FACTOR;
case AttackType.Combo2:
return SECOND_COMBO_POWER * POWER_FACTOR;
default:
// An ordinary weak punch or kick.
return ORDINARY_MOVE_POWER * POWER_FACTOR;
}
}
Dari sini terlihat, jika input method ini adalah AttackType.SpecialKick, maka outputnya adalah SPECIAL_KICK_POWER * POWER_FACTOR, di mana SPECIAL_KICK_POWER bernilai 3.0 dan POWER_FACTOR bernilai 0.5 (Lihat kembali source code lengkapnya). Dari sini, kita bisa menulis unit test dan menguji apakah benar behavior nya seperti itu. Apa yang harus kita lakukan? Sedikit perubahan pada AttackTest:
[TestMethod()]
public void AttackTest()
{
// Test for special kick.
Fighter target = new Fighter();
double expected = 1.5;
double actual = target.Attack( AttackType.SpecialKick );
Assert.AreEqual( expected, actual );
}
Gimana? Udah mulai keliatan kan alurnya? Jadi, kita deklarasikan bahwa target class nya adalah Fighter. Lalu method yang diuji adalah Attack, dengan memberikan input AttackType.SpecialKick. Kita punya ekspektasi nilai output 1.5. Baris terakhir adalah verifikasi apakah nilai output aktual sesuai dengan ekspektasi kita yang tersimpan pada variable expected. Sedikit catatan; tidak harus selalu menggunakan variable bernama expected dan actual, intinya di dalam unit test terdapat pernyataan atau verifikasi yang membandingkan ekspektasi dengan hasil aktual.
It's all set
Test method AttackTest sudah siap untuk dieksekusi. Untuk mengeksekusi sebuah test method, cukup dengan meletakkan cursor pada atau di dalam test method tersebut, lalu klik tombol Run Tests in Current Context pada toolbar.
Ketimbang klik tombol itu, saya lebih suka pake shortcut CTRL + R, T. Test method juga bisa dieksekusi melalui jendela Test List Editor. Untuk memunculkan Test List Editor, pilih menu Test --> Windows --> Test List Editor atau klik tombolnya pada toolbar.
Lalu centang test yang diinginkan --> klik kanan --> pilih Run Checked Tests.
Cara yang terakhir, bisa melalui jendela Test View. Untuk membukanya, pilih menu Test --> Windows --> Test View. Lalu klik kanan pada test method yang ingin dieksekusi --> Run Selection.
Finally, dengan perasaan penuh kebahagiaan kita melihat hasil unit test pertama kita pada jendela Test Result, ternyata Passed : )
Yep, kita telah berhasil membuat dan mengeksekusi unit test pertama dengan sukses : ) Artinya, output method Attack dengan input AttackType.SpecialKick sudah sesuai dengan ekspektasi, yaitu 1.5. Cukup? Belum tentunya. Masih ingat Code Coverage yang saya bahas sebelumnya? Ketika Code Coverage mencapai 100%, dapat diasumsikan bahwa behavior dari target method sudah sesuai dengan ekspektasi, karena semua execution path telah terlalui sesuai dengan input yang diberikan dan menghasilkan output yang sesuai.
Code Coverage Results
This is it, saatnya mengintip Code Coverage : ) Double klik LocalTestRun.testrunconfig pada Solution Items, atau bisa juga melalui menu Test --> Edit Test Run Configuration --> Local Test Run. Setelah muncul jendela baru, pilih Code Coverage, lalu centang nama target assembly kita pada bagian Select artifacts to instrument. Klik Apply, Close.
Sekarang, kita coba eksekusi AttackTest sekali lagi, masih ingat caranya kaan?! Cukup dengan meletakkan cursor pada atau di dalam method AttackTest, lalu tekan CTRL + R, T. Setelah AttackTest selesai dieksekusi, hasil Code Coverage bisa dilihat di jendela Code Coverage Results dari menu Test --> Windows --> Code Coverage Results.
Completing Unit Test
37.50%! Sejauh ini, itulah angka Code Coverage kita. Jika kita double-click pada baris highlighting tersebut, Visual Studio akan membawa kita menuju target method tersebut. Di sini terlihat, execution path mana saja yang sudah tereksekusi, dan mana saja yang belum.
Untuk membuat Code Coverage 100%, kita harus melengkapi unit test yang kita buat dengan skenario test yang diperlukan (dengan memberikan input dan ekspektasi output yang berbeda berdasarkan execution path yang ada). Di dalam unit testing, pola atau pattern seperti ini, di artikel yang ditulis oleh Marc Clifton, disebut dengan Code-Path Pattern (hehe, link nya baru dikasi ama Ka Icam.. Itu loh, seorang teman yang baru jadi artis karena menang iMULAi 2.0 ;p makasih link artikel nya ya, Ka Icaam). Jadi, method AttackTest yang lengkap (mencakup pengujian terhadap keseluruhan behavior method Attack), akan terlihat seperti ini:
/// <summary>
/// A test for Attack.
/// </summary>
[TestMethod()]
public void AttackTest()
{
// Test for special kick.
Fighter target = new Fighter();
double expected = 1.5;
double actual = target.Attack( AttackType.SpecialKick );
Assert.AreEqual( expected, actual );
// Test for special punch.
expected = 1.25;
actual = target.Attack( AttackType.SpecialPunch );
Assert.AreEqual( expected, actual );
// Test for first combo.
expected = 2.5;
actual = target.Attack( AttackType.Combo1 );
Assert.AreEqual( expected, actual );
// Test for second combo.
expected = 3.75;
actual = target.Attack( AttackType.Combo2 );
Assert.AreEqual( expected, actual );
// Test for ordinary move
expected = 1.0;
actual = target.Attack( AttackType.OrdinaryMove );
Assert.AreEqual( expected, actual );
}
Jalankan test nya sekali lagi, dan sempurna sudah Code Coverage yang kita miliki untuk method Attack. Artinya... behavior method Attack sudah teruji sepenuhnya : )
Hmmm.. apakah kita melupakan sesuatu? Oh, di class Fighter, kita masih punya method Move dan property OpponentDistance yang belum kita uji! Tapi sebentar.. property OpponentDistance?! Basically, property adalah method yang diakses sebagai data member. So, yaa.. property pun perlu diuji. Dengan adanya penambahan unit test untuk method Move dan property OpponentDistance, selain ada implementasi unit test pada method MoveTest dan OpponentDistanceTest, akan ada sedikit modifikasi pada letak deklarasi dan inisialisasi object Target. Untuk melihat hasil akhir dari class FighterTest, silahkan download solution Fighting (source code lengkap) di sini.
Jika ingin mengeksekusi semua unit test yang ada di dalam satu test class, letakkan cursor di dalam class (di luar semua test method) tersebut, lalu Run Tests in Current Context. Setelah unit test dilengkapi, pada FighterTest, Test Results dan Code Coverage (dari class Fighter) nya akan menjadi seperti ini:
Assertion
Sejauh ini, kita sudah tau, bahwa pada sebuah unit test (test method) terdapat pernyataan atau verifikasi yang membandingkan antara ekspektasi output dengan output yang sesungguhnya. Dan sejauh ini pula, kita tau bahwa proses assertion ini dilakukan oleh class Assert dengan method static nya yang bernama AreEqual. Tapi tidak hanya method AreEqual saja yang bisa kita gunakan untuk melakukan assertion, terdapat beberapa method static lainnya pada class Assert yang bisa kita gunakan. Misalnya dengan menggunakan Assert.IsTrue(). Lebih lengkapnya, silahkan merujuk ke MSDN untuk melihat keseluruhan method yang dimiliki oleh class Assert dan fungsi masing-masing method tersebut. Selain class Assert, ada dua class lagi yang bisa digunakan untuk melakukan assertion. Dua class tersebut adalah StringAssert dan CollectionAssert. Pada bagian expected exception nanti, sekalian akan saya bahas salah satu contoh penggunaan CollectionAssert.
Lebih mudahnya assertion bisa dikatakan seperti ini: jika saya melakukan sesuatu, maka saya mengharapkan terjadinya sesuatu.
Apakah assertion juga perlu dilakukan terhadap void type method? Assertion tidak hanya dilakukan atas dasar adanya return value dari sebuah method. Untuk method yang tidak menghasilkan return value (void type method), kita juga perlu melakukan assertion jika terjadi perubahan state pada member target class tersebut. Setiap member yang berubah state nya perlu kita verifikasi (baik method yang menghasilkan return value ataupun tidak). Dan dalam satu skenario test bisa dilakukan lebih dari satu assertion secara berturut-turut. Contohnya seperti ini:
[TestMethod()]
public void SetParentTest()
{
string nodeName = "data";
Node target = new Node( nodeName );
nodeName = "root";
Node parent = new Node( nodeName );
bool isSingleChild = true;
target.SetParent( parent, isSingleChild );
Assert.IsTrue( target.IsSingleChild );
Assert.IsTrue( target.HasParent );
Assert.AreEquals( parent.Name, target.Parent.Name );
}
Method SetParent dari contoh di atas adalah void type. Di dalam method tersebut terjadi perubahan state pada member IsSingleChild, HasParent, dan Parent, sehingga ketiga member tersebut perlu kita verifikasi apakah state yang terakhir sudah sesuai dengan ekspektasi kita.
Debugging Unit Test
Sama halnya dengan proses development biasa, debugging juga bisa diterapkan pada unit test dengan menggunakan Visual Studio. Tinggal pasang breakpoint pada baris yang diinginkan, letakkan cursor pada atau di dalam test method tersebut, lalu pilih menu Debug Tests in Current Context (atau dengan shortcut CTRL + R, CTRL + T).
Expected Exception
Saya asumsikan kita sama-sama sudah tidak asing lagi dengan istilah Exception : ) Dengan alasan validasi atau mekanisme error handling, method yang kita buat bisa menyebabkan munculnya exception. Sesuai dengan namanya, exception dimaksudkan untuk memberikan pengecualian (exceptional) atas state atau kondisi tertentu. Karena pengecualian tersebut juga bagian dari behavior sebuah unit (method), dan behavior tersebut merupakan ekspektasi yang kita inginkan, maka sebuah exception pun bisa kita verifikasi apakah exception tersebut muncul karena state tertentu sesuai dengan apa yang kita harapkan, atau tidak.
Misalnya saya mempunyai sebuah static class bernama Shopping dengan sebuah static method bernama AddShopItem:
public static void AddShopItem( Dictionary<string, int> shopList, string shopItem, int quantity )
{
// Validates shop list.
if( shopList == null )
{
throw new ArgumentNullException( "shopList" );
}
// Check whether shop list contains the intended shop item.
if( shopList.ContainsKey( shopItem ) )
{
// Updates shop item's quantity in the shop list.
shopList[ shopItem ] += quantity;
}
else
{
// Adds a new shop item with specified quantity.
shopList.Add( shopItem, quantity );
}
}
Terdapat validasi argumen shopList, jika null maka akan muncul ArgumentNullException. Untuk melakukan ekspektasi terhadap exception di dalam sebuah test method, kita bisa menggunakan attribute ExpectedException dengan memberikan tipe exception yang diharapkan muncul dari target unit, seperti di bawah ini:
[TestMethod]
[ExpectedException( typeof( ArgumentNullException ) )]
public void AddShopItemNullShopListTest()
{
Dictionary<string, int> shopList = null;
ShoppingUtility.AddShopItem( shopList, "Coke", 1 );
}
Tidak perlu melakukan assertion pada test method yang memiliki ekspektasi exception. Yang diperlukan, hanya memberikan input dan sebutkan tipe exception yang diharapkan. Sisanya, tinggal panggil target method nya dengan input tersebut sebagai argumen/parameter.
Sekedar contoh tambahan, untuk melengkapi unit test pada method AddShopItem, saya selipkan method AddShopItemNormalTest dengan menggunakan CollectionAssert sebagai assertion class:
[TestMethod]
[TestMethod]
public void AddShopItemNormalTest()
{
Dictionary<string, int> expectedShopList = new Dictionary<string, int>();
string shopItem = "Coke";
int quantity = 4;
expectedShopList.Add( shopItem, quantity );
Dictionary<string, int> actualShopList = new Dictionary<string, int>();
quantity = 1;
ShoppingUtility.AddShopItem( actualShopList, shopItem, quantity );
quantity = 3;
ShoppingUtility.AddShopItem( actualShopList, shopItem, quantity );
CollectionAssert.AreEqual( expectedShopList, actualShopList );
shopItem = "Tooth Brush";
quantity = 5;
expectedShopList.Add( shopItem, quantity );
ShoppingUtility.AddShopItem( actualShopList, shopItem, quantity );
CollectionAssert.AreEqual( expectedShopList, actualShopList );
}
Pengen nyobain eksekusi kedua test method ini?? Silahkan download solution Shopping (source code lengkap) di sini, dan coba sendiri : )
Test Outcome: Passed, Failed, and Inconclusive
Nah, sampai di sini, semua yang sudah saya bahas tentang unit test adalah bagaimana mencapai keberhasilan unit test, dengan indikasi hasil test yang berstatus Passed. Tapi, di balik keinginan kita membuat semua unit test menjadi Passed, terkadang kegagalan itu terjadi dengan munculnya hasil unit test yang berstatus Failed.
Perlu dicatat sebelumnya, di tulisan ini saya tidak memakai Test Driven Development (TDD). Unit test yang failed, yang paling obvious terjadi ketika ada kesalahan logic pada unit test yang kita buat, sehingga proses assertion menjadi gagal karena ekspektasi output tidak sesuai dengan output aktualnya. Atau ekspektasi exception yang kita inginkan ternyata tidak muncul, atau sebaliknya, ternyata muncul exception yang tidak diharapkan ketika kita mengeksekusi unit test. Kemungkinan lainnya, terjadi perubahan behavior pada unit, tetapi unit test yang menguji unit tersebut tidak di-update. Untuk yang satu ini terjadi karena development process yang tidak disiplin.
Seandainya method Attack pada class Fighter saya modifikasi sedikit menjadi seperti ini:
public double Attack( AttackType attackType )
{
switch( attackType )
{
case AttackType.SpecialKick:
// No need power factor for special kick.
return SPECIAL_KICK_POWER;
case AttackType.SpecialPunch:
return SPECIAL_PUNCH_POWER * POWER_FACTOR;
case AttackType.Combo1:
return FIRST_COMBO_POWER * POWER_FACTOR;
case AttackType.Combo2:
return SECOND_COMBO_POWER * POWER_FACTOR;
default:
// An ordinary weak punch or kick.
return ORDINARY_MOVE_POWER * POWER_FACTOR;
}
}
Apa yang terjadi ketika saya eksekusi test method AttackTest tanpa saya update untuk menyesuaikan behavior method Attack? Inilah yang terjadi:
Kita bisa lihat bahwa nilai ekspektasi 1.5 berbeda dengan nilai aktual, yaitu 3. Oiya, hampir lupa, untuk melihat detail dari Test Results, tinggal double klik aja test yang diinginkan dari jendela Test Results.
Inconclusive adalah status test outcome terakhir yang bisa kita dapatkan dari sebuah unit test. Secara default ketika kita membuat sebuah test class dengan menggunakan wizard dari Visual Studio, kita akan mendapatkan satu baris assertion dengan Inconclusive method. Setiap test method akan memiliki baris ini pada bagian akhir, seperti yang kita lihat pada class FighterTest di awal tadi. Status inconclusive menandakan test method yang kita miliki belum kita implementasikan, atau kita belum (atau tidak bisa) melakukan verifikasi atas behavior target method tersebut.
Disiplin itu perlu harus
Seperti yang sudah saya sampaikan sebelumnya, unit testing mendeteksi error se-dini mungkin, yang pada akhirnya akan menghasilkan code quality yang baik. Jadi, perlu ditekankan bahwa unit testing adalah suatu proses yang mandatory. Setiap unit yang dibuat harus disertai dengan unit testing. Tidak hanya membuat unit test yang diperlukan, tetapi perlu diperhatikan juga bahwa setiap terjadi modifikasi pada code kita (baik itu perubahan maupun penambahan), tidak boleh menyebabkan unit test yang sudah ada selama ini menjadi failed (breaking unit test). Practically, ketika sebuah unit baru selesai dibuat, atau terjadi perubahan terhadap sebuah unit, selain seluruh unit test untuk unit tersebut harus dipastikan passed, unit test lainnya pun harus dipastikan masih tetap passed semua. Karena, bisa jadi unit tersebut dipanggil oleh unit yang lain, sehingga secara tidak langsung menyebabkan perubahan behavior pada unit lain itu. Untuk itu, sangat direkomendasikan untuk menjalankan semua unit test yang ada pada solution kita.
Cukup tekan CTRL + R, A, maka semua unit test pada solution akan dieksekusi. Bisa juga melalui tombol Run All Tests in Solution pada toolbar, atau melalui menu Test --> Run --> All Tests in Solution.
To be continued...
Mungkin sempat terpikir, gimana caranya melakukan unit test atau assertion terhadap member yang modifier nya private... gimana ya? hehe.. ada yang disebut dengan private accessor. Dengan private accessor ini lah kita bisa mengakses semua member yang modifier nya private. Pembahasan lebih detail beserta contoh, akan saya bahas nanti. Ada juga ignoring unit test, mocking unit test dan lainnya.
Tulisan ini baru sekedar tentang bagaimana cara menghidupkan mesin motor dan mengendarainya di atas jalan yang mulus dan lurus ;D Untuk mengendarai motor tersebut di jalan yang berkelok, tidak rata, dengan tetap mengikuti rambu-rambu lalu lintas dengan baik, saya akan mencoba membahasnya pada tulisan unit testing berikutnya. Happy unit testing then... ^_^















February 5th, 2009 - 10:50
Hohohohoho… akhirnya jadi juga Unit Testing Bagian II – nya. Hmmm… Good article… Siapa tahu bisa claim:
“Extend professional activity well beyond the” + fajarsCompanyName + “work day”..
kikikikikik :D
February 5th, 2009 - 13:11
hihii… kayanya ga ngepek tu buat claim item yang itu mah ;P
March 17th, 2009 - 16:35
good article.
aku lebih sering memakai NUnit, dan untuk menjalankan unit testnya, aku rely on Resharper plugin.
Menurut aku keuntungan terbesar NUnit vs VSUnitTest adalah bisa dijalankan di Continuous Integration server tanpa menginstall Visual Studio.
March 22nd, 2009 - 19:12
@Ronald Widha:
Iya, AFAIK memang harus install Visual Studio di CI server nya ;p framework nya dari VS (Microsoft.VisualStudio.TestTools.UnitTesting namespace).
Saya pakai VSTS, TFS – TFC environment, termasuk VS unit testing karena kebetulan project saya based on Microsoft Technology (manut mau nya client saya hehe)
April 7th, 2009 - 17:35
mas..mas.. kalo bwt unit test d VB bgimana?
April 7th, 2009 - 17:36
VB .NET mksdnya..
April 7th, 2009 - 21:35
Fajar,
Saya mau cross sell sedikit nih post terbaru. Aku give example tentang unit testing dengan mocking framework, yang anda tentu setuju sangat crucial buat mendapatkan code coverage yang tinggi.
please leave a comment ;)
http://ronaldwidha.net/askbobo/articles/asp-net-mvc-witahout-the-framework-part-2-a-good-example-of-unit-testing/
April 7th, 2009 - 21:36
@Andira Muttakim, sama aja Andira.
VB dan C# pada akhirnya akan ke compile menjadi MSIL (microsoft intermediatery language). Jadi perbedaannya cuma syntax saja.
April 7th, 2009 - 23:18
@Andira:
Step nya sama kok ama C# ;D seperti kata Ronald, beda bahasa ama compiler aja.. ujung2 nya sama2 jadi MSIL : )
@Ronald:
Menuju TKP boss… tapi mungkin komen nya nyusul yaa.. udah KO nih mata, sepett banget : (
April 7th, 2009 - 23:20
Jiahh… Ronald, URL nya salah kali yak.. error euy:
HTTP Error 404.0 – Not Found
The resource you are looking for has been removed, had its name changed, or is temporarily unavailable.
July 12th, 2009 - 11:47
mw tanya mas,,
sy coba Assert.AreEqual(object, object)
trnyata failed, kan brati object1 n object2 nya gk sama,,
nah gmn caranya qt tahu dmn letak ketidaksamaannya y?
:)
thx
July 14th, 2009 - 09:41
@nueve:
object adalah reference-type. jadi, yang ai lakukan dengan Assert.AreEqual(object1, object2)..artinya ai membandingkan reference object1 dan object2 (bukan membandingkan value nya). Karena keduanya punya reference yang berbeda, so..it fails : )
Kecuali kalo keduanya punya reference yang sama.
MyClass object1 = new MyClass();
MyClass object2 = object1;
Atau.. kalo keduanya menyimpan nilai value-type (boxing).
object object1 = 3;
object object2 = 3;
Untuk reference-type, biasanya yang dibandingin itu member nya (property dan/atau private field) si object.
Assert.AreEqual(object1.Property1, object2.Property1);
Assert.AreEqual(object1.Property2, object2.Property2)
dengan catatan Property1 dan Property2 adalah value-type.
Cara lainnya bisa juga dengan melakukan override method Equals di class yang diinginkan.
public override bool Equals(object obj)
{
// Tentukan state true/false nyaL dengan membandingan member antara object ini dan object pembanding (parameter obj)
}
semoga membantu : )
July 14th, 2009 - 17:09
doumo arigatou sensei :)
ternyata gitu ya,, baru tw sy a hhee..
mantaph bener dah!