Android Unit Testing Tools

เครื่องมือที่ใช้ในการเขียน Unit Test มีหลากหลายแล้วแต่จุดประสงค์ของการทดสอบ เช่น ทดสอบ business logic, ทดสอบ UI, ทดสอบ user flow เป็นต้น 
ในที่นี้จะแบ่งเครื่องมือการทดสอบออกเป็น 2 ส่วนตามประเภทของการทดสอบ
คือ แบบ Local Unit Test Tools และ Instrumented Unit Test Tools

Part I: Local Unit Test Tools

อ่านรายละเอียดเพิ่มเติมเกี่ยวกับ Local Unit Test ได้ที่ Introduction of Unit Test for Android

แนะนำเครื่องมือที่ใช้ในการเขียน Local Unit Test
  1. JUnit เป็น Unit Test framework ที่เขียนด้วยภาษา Java เป็นส่วนหนึ่งของ xUnit และมีความสำคัญกับการพัฒนาภายใต้แนวคิด Test Driven Development (TDD) ในที่นี้จะโฟกัสไปที่ JUnit4 เนื่องจากเป็นตัวที่นิยมใช้กันในปัจจุบัน (ล่าสุดเมื่อต้นเดือนกุมภาพันธ์ที่ผ่านมา มีการ publish JUnit5 ออกมา แต่ยังเป็นเวอร์ชัน alpha)
  2. Mockito เป็นเครื่องมือที่ใช้ในการ mock data สำหรับการทดสอบเพื่อเติมเต็มส่วนที่เป็นความเชื่อมโยงกันระหว่าง Java กับ Android
Get start: Local Unit Test Implementation

การติดตั้ง Testing Environment
เพิ่ม Test framework ใน dependencies ของไฟล์ build.gradle ระดับแอปพลิเคชัน ดังนี้

การใช้งาน JUnit4
1) สร้าง public คลาสสำหรับทดสอบขึ้นมา 
2) ใช้ annotation @Test กำกับที่ด้านบนของเมธอด ทดแทนการ extends คลาส junit.framework.TestCase ใน JUnit3

ใส่ annotation @Test เพื่อระบุว่าเมธอดนี้เป็นเมธอดทดสอบ

3) การตั้งชื่อเมธอดใน JUnit4 ไม่จำเป็นจะต้องขึ้นต้นด้วย ‘test’ และสามารถสร้างหลายๆเมธอดทดสอบในคลาสเดียวกันได้

ภายในคลาสทดสอบ สามารถมีได้มากกว่า 1 เมธอดทดสอบ

4) สามารถใช้คำสั่งของ Hamcrest library ที่ JUnit4 ได้รวมเข้ามาเพื่อปรับปรุงการเขียนเทสเคสให้ดีขึ้น คือ อ่านเข้าใจง่ายขึ้น ปลอดภัยมากขึ้น ยืดหยุ่นมากขึ้น ฯลฯ (อ่านเพิ่มเติมได้ที่ The Benefits of Using assertThat over other Assert Methods in Unit Tests

การใช้งาน Mockito Framework
Mockito framework ใช้ในการ mock data ที่เป็น Android resources เพื่อกำจัด Android dependency ทำให้มั่นใจได้ว่าการทดสอบนี้เป็นการทดสอบเฉพาะโค้ด
ไม่เกี่ยวข้องกับพฤติกรรมของ Android platform
1) ใช้ annotation @RunWith(MockitoJUnitRunner.class)กำกับที่ด้านบนของคลาส
2) ใช้ annotation @Mock สร้าง mock object สำหรับ Android dependency
3) สามารถกำหนด condition และ return value ด้วยคำสั่ง when() และ thenReturn() ตามลำดับ

ตัวอย่างการใช้งาน Mockito ในที่นี้ใช้ mock object Context ของแอนดรอยด์

รูปที่ 1 แสดงการใช้ Mockito ในการ mock up ตัวแปร Context
รูปที่ 2 คลาสที่ต้องการทดสอบ ซึ่งมีการอ่านค่า String จาก Context

จากตัวอย่างโค้ดข้างต้น ในคลาส ExampleUnitTest มีการ mock ตัวแปร mockupContext ขึ้นมาและในเมธอด readStringFromContext() ได้กำหนดเงื่อนไขไว้ว่า when(mockUpContext.getString(R.string.app_name))
.thenReturn(FAKE_STRING);

นั่นคือ เมื่อมีการเรียกคำสั่ง context.getString(R.string.app_name)จะ return ค่า FAKE_STRING ไปแทนค่าจริง ดังนั้นเมื่อส่ง mockUpContext เข้าไปในคลาสที่ต้องการทดสอบในที่นี้คือ ClassUnderTest แล้วเรียกเมธอด getAppName() ที่มีคำสั่ง context.getString(R.string.app_name)ค่าที่รีเทิร์นจากเมธอด getAppName() จะเป็นค่าที่ถูกกำหนดไว้ตามเงื่อนไข นั่นคือ ค่าของ FAKE_STRING หลังจากได้ result มาก็ทำการทดสอบว่าค่าที่ได้นั้นเป็น FAKE_STRING หรือไม่ ด้วยคำสั่ง assertThat(resultm is(FAKE_STRING))

** ศึกษาการใช้งาน Mockito framework เพิ่มเติมได้ที่ Mockito API reference**

การรัน Local Unit Tests

การรัน Local Unit Test สามารถทำได้โดยการคลิกขวาที่ directory, class หรือ method ที่ต้องการรันการทดสอบ ตามแต่วัตถุประสงค์และผลลัพธ์ของการทดสอบที่ต้องการ

Part II: Instrumented Unit Test Tools

อ่านรายละเอียดเพิ่มเติมเกี่ยวกับ Instrumented Unit Test ได้ที่ Introduction of Unit Test for Android

แนะนำเครื่องมือที่ใช้ในการเขียน Instrumented Unit Test
  1. AndroidJUnitRunner เป็น Test runner ที่สามารถรัน JUnit3 หรือ JUnit4 บน Android devices ซึ่งจะประกอบไปด้วย UI Testing Framework อีกสองตัวคือ
  • Espresso 
    ** requires Android 2.2 (API level 8) or higher **
    มีชุด APIs สำหรับทดสอบ user flows ภายในแอปพลิเคชัน เหมาะกับการเขียน การทดสอบแบบ white box-style
  • UI Automator 
    ** requires Android 4.3 (API level 18) or higher **
    เป็น Testing framework ที่เป็นส่วนหนึ่งของ Android SDK Manager มีชุด APIs ที่สามารถทำการทดสอบหรือจำลองการใช้งานของผู้ใช้ตั้งแต่เปิดแอปพลิเคชันจนจบการทำงานของแอปพลิเคชัน มี GUI ในการแสดงผลและอ่านค่าของ components ต่างๆ ที่เกิดขึ้นกับหน้า interface ที่แสดงผลอยู่ในขณะนั้น เหมาะสำหรับการเขียนการทดสอบแบบ black box-style

สำหรับรายละเอียดของ UI Testing framework ทั้งสองตัวนี้จะขอแยกออกไปในบทความถัดไป ในหัวข้อ UI Testing

Get start: Instrumented Unit Test Implementation

การติดตั้ง Testing Environment
1) เพิ่ม androidTestCompile ไว้ใน dependencies ของไฟล์ build.gradle ในระดับแอปพลิเคชัน ดังนี้

2) กำหนดค่าตั้งต้นของ test instrumentation runner เป็น AndroidJUnitRunner เพื่อใช้คลาสทดสอบต่างๆของ JUnit4 โดยการเพิ่ม defaultConfig ในไฟล์ build.gradle ระดับ module ของแอปพลิเคชัน ดังนี้

สร้างคลาส Instrumented Unit Test

ในการสร้างคลาสทดสอบแบบ Instrumented Unit Test ต้องเพิ่ม annotation @RunWith(AndroidJUnit4.class)บนชื่อคลาส

โค้ดแสดงตัวอย่างการเขียน Instrumented Unit Test ที่มีการใช้ SharedPreferences ของแอนดรอยด์
สร้าง Test suite

Test suite คือ collection ของคลาสทดสอบ ใช้ในการจัดคลาสทดสอบหลายๆคลาสไว้รันด้วยกัน ตาม convention package name ของคลาส test suite จะปิดท้ายด้วย .suite เช่น net.gawvie.droid.bearbox.suite เป็นต้น
 การสร้างคลาส test suite จะต้องระบุ annotation 2 อย่าง คือ@RunWith(Suite.class)
@Suite.SuiteClasses({List of test classes in suite})

ตัวอย่างคลาส test suite ที่ประกอบไปด้วยคลาสทดสอบ 2 คลาส
การรัน Instrumented Unit Tests

เหมือนกับการรัน Local Unit Tests แต่จะต้องต่อกับ device หรือ emulator เพื่อรันทดสอบ และสามารถรันทดสอบได้ทั้งในระดับเมธอด คลาส test suite หรือ directory ตามแต่จุดประสงค์ของการรันทดสอบแต่ละครั้ง


บทความนี้ต้องการเป็นเพียงจุดเริ่มต้นของคนที่สนใจจะเขียน Unit Test แต่ยังไม่มีประสบการณ์มาก่อน ตัวอย่างที่ยกมาข้างต้น เป็นเพียงส่วนนึงที่เป็นพื้นฐาน เพื่อใช้อธิบายให้เกิดความเข้าใจง่ายๆ เป็นไอเดียในการเริ่มเขียน Unit Test เท่านั้น สำหรับผู้ที่สนใจรายละเอียดเพิ่มเติม สามารถศึกษาได้จาก link ของเวปไซต์หรือบทความที่แนบไว้ในบทความนี้หรือใช้ keyword ในบทความนี้ไปค้นคว้าเพิ่มเติมต่อไป

References: