Test Driven Development နဲ့ကျွန်တော်
လွန်ခဲ့တဲ့ငါးနှစ်ကျော်လောက်က စပြီးတော့ TDD ဆိုတဲ့ Practice အကြောင်းကို စပြီး ထိတွေ့မိတယ်။ ကျွန်တော်နားထောင်နေကျ Programming Conference Talk တွေ မှာ ထည့်ထည့်ပြောကြတယ်။
အဲ့ဒါနဲ့ ကျွန်တော်လည်း စပြီး Unit Test လေးတွေရေးကြည့်တယ်။ စစချင်းကြုံရတဲ့ ပြဿနာက ဘာကို Test လုပ်ရမှန်းမသိတာပဲ။ Test အရင်ရေးပြီး ကုတ်ရေးဖို့နေနေသာသာ ကိုယ်ခုတင်ရေးထားတဲ့ကုတ်ကို ဘယ်လိုစပြီး Test လုပ်ရမလဲမသိဘူး။
အဲ့ဒါနဲ့ TDD Tutorial တွေ လိုက်ကြည့်၊ Mock တွေ Stub တွေ Fake တွေ အစသဖြင့် Jargon တွေတော့ သိလာတယ်။ လွယ်လွယ်ကူကူ Model Class လေးတွေ Validation Logic တွေကို Test ရေးတတ်လာတယ်။ ဒါပေမဲ့ ကိုယ် Test လုပ်ချင်တဲ့ ကုတ်က ရှုပ်ရှုပ်ရှက်ရှက် Set Up တွေများမယ်၊ တကယ်စစ်ရမဲ့ရလဒ်က ခက်ခက်ခဲခဲစစ်ရမယ်ဆိုမလုပ်တတ်တော့ဘူး။
အဲ့တော့ Feature လေးတွေအရင်ရေးတယ်။ နောက် Test လေးတွေလိုက်ထည့်တယ်။ အဲ့လိုနဲ့ Regression Test တွေရဲ့ အားသာချက်က စပြီး တန်ဖိုးသိလာတယ်။ အထူးသဖြင့် ကိုယ်ရေးထားပြီးသား ကုတ်ကို ပြန်ပြင်ရတဲ့အချိန်မှာ စိတ်ချလက်ချပြင်လို့ရတာကို စကြိုက်လာတယ်။
နောက် ကိုယ် Join ရတဲ့ကုမ္ပဏီက CI/CD Pipeline နဲ့ Test Suite Green မဖြစ်ရင် ကိုယ့် PR ကို ပေး မ Merge တဲ့ အလုပ်ဖြစ်လာတော့ Unit Testing ရော Integration Testing ရောပိုလုပ်ဖြစ်လာတယ်။ Unit Test ရေးပါများလာတော့ Fake/Stub/Mock တွေ သုံးဖူးလာတယ်။ Collaborator တွေ Setup သိပ်ရှုပ်နေရင် Mock လုပ်တော့တာပဲ။
Fake, Stub, Mock, Spy ဘာတွေလဲ
Fake ဆိုတာက ကိုယ် isolate လုပ်ပြီး Test ချင်တဲ့ Class က တစ်ခြား External Collaborator Object တစ်ခုကိုလိုအပ်တယ်၊ ဒါပေမဲ့ အဲ့ဒီ Object အစား အဲ့ဒီလိုပဲ ပြုမူတဲ့ object တစ်ခု အစားထိုးထည့်ပေးလိုက်တာမျိုး။ ဥပမာ တကယ်ဖိုင်မှာသွားသိမ်းရမဲ့ Object အစား Memory မှာပဲသိမ်းတဲ့ Object နဲ့အစားထိုးထည့်လိုက်တာမျိုးပေါ့။ ကိုယ်စစ်ချင်တာက သူဘယ်မှာသွားသိမ်းလဲဆိုတာမဟုတ်လို့ လွယ်တဲ့ဟာနဲ့ အလုပ်ဖြစ်အောင် အစားထိုးလိုက်တာပဲ။
Stub တွေက Fake နဲ့ ခပ်ဆင်ဆင်ပဲ။ များသောအားဖြင့် Collaborator Object တစ်ခုခုဆီက ရလဒ်တစ်ခုခုလိုချင်တယ် ဒါပေမဲ့ အဲ့ရလဒ်က ဘယ်လိုရလာရလာ အရေးမကြီးဘူးဆိုရင် အဲ့ဒီရလဒ် လိုချင်တဲ့ Message(Method) ကို Stub လုပ်ပြီး အသင့်သုံးလို့ရတဲ့ Value တစ်ခု Return ပြန်လိုက်တယ်။
Mock ဆိုတာက တော့ ပိုပြီးအတွင်းကျကျ Control လုပ်ချင်ရင်သုံးတယ်။ ကိုယ့် implementation မှာ တစ်ခြား Object ကို Message တွေပို့တယ်။ ဒါပေမဲ့ အဲ့ဒီ Object က အဲ့ Message ပို့ရင် ဘာလုပ်လဲဆိုတာက အရေးမကြီးတဲ့ အတွက် Message ပို့တယ်ဆိုတဲ့ Behavior ကို ပဲ Verify လုပ်လိုက်တယ်။
နောက် Spy ဆိုတာရှိသေးတယ်။ သူကတော့ လုံးဝအတွင်းကျကျ ဒီ Class က ဒီ Method ကို ဘယ်နှစ်ကြိမ်ခေါ်ရမယ်။ ဘာ Argument တွေပေးပြီး ခေါ်ရမယ်ဆိုတာမျိုးကို စောင့်ကြည့်တာမျိုး။ ဖြစ်နိုင်ရင်တော့ Spy တွေသုံးရတာမကြိုက်ဘူး။ သူတို့ ကို သုံးပြီး Test လုပ်ထားရင် ကိုယ့်ရဲ့ Implementation က Rigid ဖြစ်လာတယ်။ Implementation ပြင်ရင် Behavior မပြောင်းပဲနဲ့တောင် Test ကိုလိုက်ပြောင်းရတာမျိုးတွေဖြစ်တတ်တော့ သိပ်မကြိုက်ဘူး။ အတတ်နိုင်ဆုံး မသုံးဖို့ကြိုးစားတယ်။
ပထမဆုံးရေးတဲ့ test ကို တစ်သက်လုံးဖက်တွယ်ထားစရာမလို
နောက်ပိုင်း Gary Benhardt ရဲ့ DestroyAllSoftware screencast တွေကြည့်ဖြစ်တဲ့အချိန်မှာ အားကျရတယ်။ Test ကိုအရင်ရေး ကိုယ်ဖြစ်ချင်တဲ့ Design ကို စရေးပြီး Class Design တွေတစ်ဖြည်းဖြည်း တည်ဆောက်သွားတာတွေလေ့လာရတယ်။ သူ့ဆီကနေ Smoke Testing နဲ့ Throw Away Test တွေရေးတာ ဆည်းပူးရတယ်။ Smoke testing ကတော့ ကိုယ့်ကုတ်ကို ထောင့်စေ့အောင် Test မရေးပဲ Happy Path ကို ပဲ စစ်ထားတဲ့ Test မျိုး။ အနည်းဆုံးတော့ ပုံမှန် Run ရမဲ့ Happy Path ကမှန်နေသေးတယ်ဆိုတဲ့ စိတ်ချရမှုတစ်ခုရယူတာမျိုး။ ကိုယ် Test လုပ်ရမဲ့ကုတ်က Conditional Branching အရမ်းများနေတာ ဒါမှမဟုတ် Input Combination က အရမ်းများနေတာမျိုးဆိုရင် Smoke Testing လုပ်ပြီး Detail အဆင့်တွေမှာ refactor လုပ်လို့ရအောင် လုပ်ယူတာမျိုးတွေမှာ သုံးတယ်။
Throw Away Test ဆိုတာက ကိုယ် Test Drive လုပ်ချင်တဲ့ Feature က ဘာလုပ်ချင်မှန်းရေရေရာရာ မသိသေးတဲ့အချိန် စမ်းပြီးရေးကြည့်တဲ့ Test မျိုး၊ အဲ့ဒီ Test ကို pass အောင်လုပ်ရင်းနဲ့ Implementation မှာ Design ကတစ်ဖြည်းဖြည်းရုပ်လုံးပေါ်လာပြီး ကိုယ်ရေးချင်တဲ့ Feature ကိုပိုနားလည်လာတယ်။ အဲ့တော့မှ ကနဦး Test တွေကိုဖျက်ပြီး ကိုယ်ရေးချင်တဲ့ Feature ကို Isolate လုပ်ပိုပြီး Granular ဖြစ်တဲ့ Test တွေထပ်ထည့်တာမျိုး လုပ်လို့ရတယ်။
အဲ့ဒီအရင်က ကုတ်ဆိုတာကို ရေးပြီးရင်ဘယ်တော့မှ ပြန်မဖျက်ချင်ဘူး။ Sunk Cost Fallacy လို့ခေါ်တဲ့ ကိုယ်အားစိုက်ထုတ်ပြီးရေးထားရတာကို ကောင်းကောင်းမကောင်းကောင်း ရေးပြီးပြီပဲဆိုပြီး ဆက်ပြီး သုံးနေချင်သေးတာ။
Refactoring လုပ်ရင်ပိုသိသာ
တစ်ဖြည်းဖြည်း မဖြစ်မနေ test ရေးရတော့ တစ်ဖြည်းဖြည်းနဲ့ test အရင်ရေးပြီးမှကုတ်ရေးတာတွေစလုပ်တတ်လာတယ်။ feature တစ်ခုခုအသစ်ရေးတော့မယ်ဆိုရင် test ဘယ်လိုရေးရမလဲ စစဉ်းစားတတ်လာတယ်။ သူများကုတ်ကို ပြင်တော့မယ်ဆိုရင်လည်း test coverage ကို စစ်တတ်လာတယ်။
အထူးသဖြင့် Martin Fowler ရဲ့ Refactoring စာအုပ်ရယ်။ Sandi Metz ရဲ့ Object Oriented Design talk တွေ၊ Katrina Owen ရဲ့ Therapeutic Refactoring တွေနားထောင်ပြီးတော့ Refactoring လုပ်တာကို ကြိုက်လာတယ်။ Refactor လုပ်ပြီဆိုရင်က test coverage မရှိမဖြစ်လိုလာပြီ။ test မရှိပဲ refactor လုပ်ရင် confidence မရှိဘူး။ ဘာလို့လဲဆိုတော့ refactoring လုပ်တယ်ဆိုတာ ကုတ်ကို behavior မပြောင်းပဲ structure ပဲပြောင်းအောင်လုပ်တာဖြစ်လို့ test မရှိရင် behavior ပြောင်းသွားလားမပြောင်းသွားလား သိဖို့မလွယ်ဘူး။ အဲ့တော့ သူများကုတ်ပဲဖြစ်ဖြစ် ကိုယ့်ကုတ်ပဲဖြစ်ဖြစ် refactor လုပ်တော့မယ်ဆိုရင် test coverage လုံလောက်အောင်စလုပ်တတ်လာတယ်။
Test ကပေးတဲ့ Signal
နောက်တစ်ခုသတိထားမိတာက တစ်ချို့ Testing Pitfall တွေကို တဖြည်းဖြည်းမြင်တတ်လာတာပဲ။ ကိုယ် Test နေတဲ့ ကုတ်ကိုပဲ ကိုယ့်ဘာသာ ပြန် Stub လုပ်ထားတဲ့ Test တို့၊ Assertion ကကိုယ် ပေးလိုက်တဲ့ Test Value ကိုပဲ ပြန် Assert လုပ်ထားတာတို့ဆိုရင် ချက်ချင်း Test Smell အဖြစ်ခံစားတတ်လာတယ်။ သိပ်ခက်ခက်ခဲ Test နေရတဲ့ ကုတ်တွေဆိုရင် တစ်ခုခုတော့မှားနေပြီဆိုပြီးတွေးတတ်တဲ့ Intuition ခေါ်မလား အကျင့်ခေါ်မလား ဖြစ်လာတယ်။ နောက်ပိုင်း တကယ်တော့ Test Driven Development ဆိုတာ Test ရေးရင်းနဲ့ Test ပေးတဲ့ Signal တွေကိုကြည့်ပြီး Software ရဲ့ Design Health ကို သိအောင်လုပ်တာပါလားလို့သိလာတယ်။
ခု ၅နှစ်ကျော်ကြာခဲ့ပြီ Test Driven Development ကိုလေ့ကျင့်နေရတုံးပဲ။ ထပ်လေ့လာစရာတွေလည်း ရှိနေသေးတယ်။ အရင်တုံးက သိပ်ဆက်စပ်မှုမရှိဘူးလို့ထင်ထားတဲ့ အစိတ်အပိုင်းတွေက ဟိုနားဒီနား နည်းနည်းစီတော့ ရုပ်လုံးပေါ်လာတယ်။ Object Oriented Design, Design Patterns, Test Driven Development, Refactoring စတဲ့ concept တွေ အစကတော့ ဘာကြောင့်လဲဆိုတာသိပ်နားမလည်။ ကိုယ်လေးစားအားကျရတဲ့ ပညာရှင်တွေကကောင်းတယ်ပြောလို့ လိုက်လုပ်ကြည့်တာပဲ။ လူကြားထဲမှာ အာရွှီးကောင်းရုံပဲ။ ခုတော့ ဒီလူတွေ သူတို့ အတွေ့အကြုံတွေရဲ့ အနှစ်သာရတွေ လက်ဆင့်ကမ်းနေတာပဲ ဆိုတာ ပိုပိုပြီးတန်ဖိုးထားတတ်လာပါပြီ။ လျစ်လျူရှုထားတော့လည်း ကိုယ့်ဘာသာကိုယ် ဒီအဖြေရောက်အောင် ခက်ခက်ခဲခဲ ရှာနေရတာပဲကိုး။