Java JUnit Unit Testing: මෘදුකාංග ගුණාත්මකභාවය ඉහළට SC Guide

Java JUnit Unit Testing: මෘදුකාංග ගුණාත්මකභාවය ඉහළට SC Guide

කොහොමද යාලුවනේ! ❤️

අද අපි කතා කරන්න යන්නේ මෘදුකාංග සංවර්ධනයේ (Software Development) ගොඩක්ම වැදගත් දෙයක් ගැන. ඒ තමයි Unit Testing. විශේෂයෙන්ම Java Development වලදි නැතුවම බැරි දෙයක් තමයි JUnit පාවිච්චි කරලා Unit Tests ලියන එක.

කෝඩ් ලියනකොට, විශේෂයෙන්ම ලොකු ප්‍රොජෙක්ට් වල වැඩ කරනකොට, පොඩි වැරැද්දක් උනත් මුළු සිස්ටම් එකම අවුල් වෙන්න පුළුවන්. හිතන්න, ඔයා ලොකු ගොඩනැගිල්ලක් හදන ඉංජිනේරුවෙක් කියලා. ගොඩනැගිල්ල හදලා ඉවර වෙලා නෙවෙයිනේ බිත්තියක ගඩොල් හරිද කියලා බලන්නේ? ගඩොලක් තියනකොටම ඒක හරියට තියලා තියනවද කියලා චෙක් කරනවා වගේ වැඩක් තමයි මේ Unit Testing කියන්නේ. අපේ කෝඩ් එකේ පොඩිම පොඩි කොටස් (Units) හරිද කියලා, ඒ මොහොතෙම චෙක් කරන එක තමයි මෙයින් වෙන්නේ. එතකොට බග්ස් (Bugs) එන්න කලින්, පොඩි කාලෙදීම අඳුරගෙන හදාගන්න පුළුවන්.

මේ වගේ දේවල් වලට තමයි JUnit වගේ Frameworks අපිට උදව් කරන්නේ. එහෙනම්, අපි බලමු මේ වැඩේ කොහොමද කරන්නේ කියලා.

Unit Testing කියන්නේ මොකක්ද? (What is Unit Testing?)

සරලවම කිව්වොත්, Unit Testing කියන්නේ ඔයාගේ Application එකේ තියෙන පොඩිම පොඩි, තනියම Test කරන්න පුළුවන් කොටස (a Unit) හරිද කියලා බලන එක. මේ Unit එකක් කියන්නේ එක Method එකක් වෙන්න පුළුවන්, එහෙම නැත්නම් එක Class එකක් වෙන්නත් පුළුවන්. මේ Test එකේ අරමුණ තමයි, ඒ Unit එකෙන් අපි බලාපොරොත්තු වෙන ප්‍රතිඵලය හරියටම ලැබෙනවද කියලා බලන එක.

  • හිතාගන්න පුළුවන්: ඔයා ගණන් හදන Class එකක් ලිව්වා කියලා. ඒකේ Add, Subtract වගේ Methods ඇති. Unit Test එකකින් කරන්නේ, Add Method එකට 2යි 3යි දුන්නම 5 එනවද කියලා බලන එක.
  • ඉක්මන්: මේ Tests ගොඩක් වේගයෙන් දුවනවා. ඒ නිසා ඔයාට කෝඩ් එකේ පොඩි වෙනසක් කරාම වුනත්, ඉක්මනින්ම Test කරලා බලන්න පුළුවන්.
  • බග්ස් ඉක්මනින් හොයාගන්නවා: කෝඩ් එක ලිව්වට පස්සේ බග්ස් හොයාගන්න යනවාට වඩා, මේ වගේ පොඩි පොඩි කොටස් Tests කරන එකෙන්, බග්ස් මුල් අවස්ථාවේදීම අඳුරගෙන හදාගන්න පුළුවන්. ඒකෙන් ලොකු කාලයක් සහ මහන්සියක් ඉතුරු වෙනවා.

JUnit මොකටද? (Why JUnit for Java?)

හරි, දැන් ඔයාලා හිතනවා ඇති මේවාට අපිට මොනවාද කරන්න පුලුවන් කියලා. ඔන්න ඕකට තමයි Java වලට සුපිරිම Framework එකක් තියෙන්නේ. ඒ තමයි JUnit! JUnit කියන්නේ Java වල Unit Testing වලට තියෙන Standard Framework එක. මේක කොච්චර ලේසිද කියනවා නම්, බිස්කට් එකක් කනවා වගේ වැඩක්. 😂

JUnit වලින් ලැබෙන ප්‍රධානම වාසි ටිකක් තමයි මේ:

  • භාවිතා කිරීමට පහසුයි (Easy to Use): Annotations (උදා: @Test) වගේ දේවල් නිසා Test Cases ලියන එක ගොඩක් ලේසියි.
  • ස්වයංක්‍රීයකරණය (Automation): Tests ස්වයංක්‍රීයව දුවවන්න පුළුවන්. ඒ කියන්නේ, කෝඩ් එකේ වෙනසක් කරාම ආයෙත් අතින් Test කරන්න ඕනේ නෑ.
  • ගැළපෙන බව (Integration): Eclipse, IntelliJ IDEA වගේ IDEs (Integrated Development Environments) වලට වගේම Maven, Gradle වගේ Build Tools වලටත් හොඳටම ගැළපෙනවා.
  • ක්‍රියාකාරීත්වය (Robustness): ලොකු ප්‍රොජෙක්ට් වලට වුණත් හොඳටම ගැලපෙනවා.

සරල JUnit Test එකක් ලියමු! (Let's Write a Simple JUnit Test!)

හොඳයි, දැන් අපි බලමු ඇත්තටම කෝඩ් එකක් ලියලා මේ JUnit වැඩේ කොහොමද කරන්නේ කියලා. අපි හිතමු අපිට පොඩි Calculator Class එකක් තියෙනවා කියලා. ඒකේ add, subtract වගේ Methods තියෙනවා.

පළමුව, Test කරන්න අවශ්‍ය Class එක (The Class to Test)

අපි මේ Class එක src/main/java/com/example/Calculator.java කියලා හදාගමු:

package com.example;

public class Calculator {

    public int add(int a, int b) {
        return a + b;
    }

    public int subtract(int a, int b) {
        return a - b;
    }

    public boolean isEven(int number) {
        return number % 2 == 0;
    }
}

JUnit Dependency එක Add කරගනිමු (Add JUnit Dependency)

ඔයා Maven පාවිච්චි කරනවා නම්, pom.xml එකට මේ ටික එකතු කරන්න:



    org.junit.jupiter
    junit-jupiter-api
    5.10.0
    test


    org.junit.jupiter
    junit-jupiter-engine
    5.10.0
    test

Gradle නම්, build.gradle එකට මේ ටික එකතු කරන්න:

dependencies {
    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.10.0'
    testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
}

දැන් Test Class එක ලියමු (Now, Write the Test Class)

Test Classes සාමාන්‍යයෙන් src/test/java ෆෝල්ඩර් එකේ තමයි හදන්නේ. අපේ Class එකට CalculatorTest.java කියලා නම දෙමු.

package com.example;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class CalculatorTest {

    @Test
    void testAdd() {
        Calculator calculator = new Calculator();
        int expected = 5;
        int actual = calculator.add(2, 3);
        assertEquals(expected, actual, "2 + 3 should be 5");
    }

    @Test
    void testSubtract() {
        Calculator calculator = new Calculator();
        assertEquals(1, calculator.subtract(5, 4), "5 - 4 should be 1");
    }

    @Test
    void testIsEven() {
        Calculator calculator = new Calculator();
        assertTrue(calculator.isEven(4), "4 should be an even number");
        assertFalse(calculator.isEven(3), "3 should not be an even number");
    }

    @Test
    void testAddNegativeNumbers() {
        Calculator calculator = new Calculator();
        assertEquals(-5, calculator.add(-2, -3), "Adding two negative numbers");
        assertEquals(1, calculator.add(5, -4), "Adding positive and negative numbers");
    }

    @Test
    void testDivideByZero() {
        // This test will fail if Calculator doesn't handle division by zero
        // In a real scenario, you'd test if an expected exception is thrown.
        // assertThrows(ArithmeticException.class, () -> calculator.divide(10, 0));
        // For now, we only have add/subtract/isEven
    }
}

Test එකේ කොටස් ගැන තේරුම් ගනිමු (Understanding the Test Parts)

  • @Test Annotation: මේක තමයි JUnit වලට කියන්නේ මේ Method එක Test එකක් කියලා. මේක නැත්නම් JUnit මේ Method එක Test කරන්නේ නෑ.
  • Assertions: Test එකේ ප්‍රතිඵලය හරිද වැරදිද කියලා බලන්න පාවිච්චි කරන Methods තමයි මේවා.
    • assertEquals(expected, actual, message): අපිට බලාපොරොත්තු වෙන අගය (expected) සහ ඇත්තටම ලැබුණු අගය (actual) සමානද කියලා බලනවා. සමාන නැත්නම් Test එක Fail වෙනවා. message කියන එක Test එක Fail වුනාම පෙන්නන Message එක.
    • assertTrue(condition, message): දීපු condition එක true ද කියලා බලනවා.
    • assertFalse(condition, message): දීපු condition එක false ද කියලා බලනවා.
    • තව assertNotNull, assertNull, assertThrows වගේ ගොඩක් Assertions තියෙනවා.

දැන් ඔයාට ඔයාගේ IDE එකෙන් (Eclipse, IntelliJ IDEA) මේ Test Class එක right click කරලා Run කරන්න පුළුවන්. එහෙම නැත්නම් Maven වගේ Build Tool එකකින් mvn test කියලා Command එක ගැහුවම Test දුවනවා.

JUnit වල තව විශේෂාංග (More JUnit Features)

JUnit වල මේ වගේ තව ගොඩක් විශේෂාංග තියෙනවා, ඒවත් දැනගන්න එක වටිනවා.

  • @DisplayName: මචං, Test එකට ලස්සන නමක් දෙන්න පුළුවන්. Test Results Report එක බලනකොට තේරුම් ගන්න ලේසියි.
  • @Disabled: මේ Test එක තාවකාලිකව Disable කරන්න මේක පාවිච්චි කරන්න පුළුවන්. ඒ කියන්නේ, මේ Test එක පස්සේ දුවවමු කියලා ඔයාට හිතුනොත්.
  • @BeforeEach and @AfterEach: සමහර වෙලාවට Test එකක් දුවන්න කලින් පොඩි Setup එකක් කරන්න ඕනේ. (උදා: Database connection එකක් හදාගන්න, Object එකක් Initialize කරගන්න). ඒ වගේම Test එක දුවලා ඉවර වුනාම Clean-up එකක් කරන්නත් (උදා: Database connection එක Close කරන්න) මේ Annotations පාවිච්චි කරන්න පුළුවන්. @BeforeEach එකෙන් සෑම Test Method එකකටම කලින් දුවන කෝඩ් එකක් ලියන්න පුළුවන්, @AfterEach එකෙන් සෑම Test Method එකකින්ම පස්සේ දුවන කෝඩ් එකක් ලියන්න පුළුවන්.
  • assertThrows(ExpectedException.class, () -> methodCall()): අපේ කෝඩ් එකෙන් යම්කිසි වෙලාවකදී යම්කිසි Exception එකක් (Error එකක්) එනවද කියලා Test කරන්න මේක පාවිච්චි කරනවා. උදාහරණයක් විදියට, බිංදුවෙන් බෙදනකොට ArithmeticException එකක් එනවද කියලා බලන්න පුළුවන්.

ප්‍රයෝජන සහ හොඳ පුරුදු (Benefits and Best Practices)

Unit Testing වලින් ලැබෙන ප්‍රයෝජන සහ ඒක කරනකොට මතක තියාගන්න ඕනේ හොඳ පුරුදු ටිකක් මේ.

ප්‍රයෝජන (Benefits)

  • බග්ස් ඉක්මනින් හඳුනාගැනීම (Early Bug Detection): කෝඩ් එකේ වැරදි මුලින්ම හඳුනාගන්න පුළුවන් නිසා ඒවා හදන්න යන කාලය සහ වියදම අඩු වෙනවා.
  • කෝඩ් Refactor කිරීමට පහසුයි (Facilitates Refactoring): ඔයාට කෝඩ් එකේ ව්‍යුහය (Structure) වෙනස් කරන්න (Refactor කරන්න) බය වෙන්න ඕනේ නෑ. Tests තියෙන නිසා, වෙනසක් කරාම කෝඩ් එක කැඩුණද කියලා ඉක්මනින් දැනගන්න පුළුවන්.
  • හොඳ Documentation එකක් (Good Documentation): Test Cases කියන්නේ ඔයාගේ කෝඩ් එකෙන් මොකක්ද වෙන්න ඕනේ කියලා පෙන්වන ජීවමාන Documentations වගේ. අලුතින් ප්‍රොජෙක්ට් එකට එන කෙනෙකුට Tests බැලුවම කෝඩ් එක තේරුම් ගන්න ලේසියි.
  • විශ්වාසය වැඩි කරනවා (Increases Confidence): Tests හරියට දුවනවා නම්, අපේ කෝඩ් එක වැඩ කියලා හිතට සැනසීමක් එනවා. අලුත් ෆීචර්ස් (Features) එකතු කරනකොටත්, පරණ ඒවා වැඩ කරනවද කියලා සැකයක් නෑ.
  • Design Quality එක වැඩි කරනවා (Improves Design Quality): Testable කෝඩ් එකක් ලියන්න හිතනකොට, හොඳ Design Patterns පාවිච්චි කරන්න හිතෙනවා. ඒකෙන් Overall කෝඩ් Quality එක වැඩි වෙනවා.

හොඳ පුරුදු (Best Practices)

  • AAA Pattern (Arrange, Act, Assert): මේක Unit Tests ලියනකොට පාවිච්චි කරන ප්‍රසිද්ධ රටාවක්.
    • Arrange: Test කරන්න අවශ්‍ය දේවල් (Objects, Variables) ලෑස්ති කරනවා.
    • Act: ඇත්තටම Test කරන්න ඕනේ Method එක Call කරනවා.
    • Assert: ප්‍රතිඵලය හරියටම අපිට අවශ්‍ය විදියට ආවද කියලා බලනවා (Assertions පාවිච්චි කරලා).
  • Independent Tests: Test එකක් තව Test එකකට බලපාන්න හොඳ නෑ. සෑම Test එකක්ම තනියම දුවන්න පුළුවන් වෙන්න ඕනේ.
  • Fast Tests: Tests ගොඩක් ඉක්මනින් දුවන්න ඕනේ. එතකොට Developers ලට නිතරම Tests දුවවලා බලන්න පුළුවන්.
  • Clear Test Names: Test එකෙන් මොකක්ද වෙන්නේ කියලා නමෙන් තේරෙන්න ඕනේ. උදා: testAddTwoPositiveNumbers().
  • Single Responsibility Principle (SRP): සෑම Test එකකින්ම Test කරන්න ඕනේ එකම දෙයක්.

අවසන් වශයෙන් (Conclusion)

Unit Testing කියන්නේ මෘදුකාංග සංවර්ධනයේදී නැතුවම බැරි දෙයක්. විශේෂයෙන්ම Java Development වලදී JUnit Framework එකෙන් මේ වැඩේ ගොඩක් පහසු කරලා තියෙනවා. මේකෙන් අපේ කෝඩ් එකේ Quality එක වැඩි වෙනවා වගේම, බග්ස් හොයාගෙන හදන්න යන කාලයත් ඉතුරු වෙනවා. ඒ වගේම, කෝඩ් එකට වෙනස්කම් කරන්න සහ අලුත් ෆීචර්ස් එකතු කරන්නත් බය නැතුව පුළුවන්.

ඉතින් යාලුවනේ, ඔයාලා අද ඉගෙනගත්ත දේවල් ප්‍රායෝගිකව කරලා බලන්න. පොඩි පොඩි කෝඩ් කෑලි වලට Test Cases ලියන්න පුළුවන්. එතකොට ටිකෙන් ටික ඔයාට මේක හොඳටම හුරු වෙයි. මතක තියාගන්න, හොඳම ඉගෙනගැනීම තමයි අත්දැකීමෙන් ලබන එක.

ඔයාලා මේක Try කරලා බැලුවද? මොනවද ප්‍රශ්න ආවේ? එහෙම නැත්නම් ඔයාලගේ අදහස් මොනවද? Comment section එකේ අපිට කියන්න. අපි ඊළඟ ලිපියකින් හමුවෙමු! Happy Coding! 👋