Maven ใช้งาน part 2 — with Tests

Phai Panda
3 min readJul 8, 2020

--

งานอย่างหนึ่งที่สำคัญสำหรับการพัฒนาโปรแกรมก็คือการเขียนเทส เป็นการการันตีความถูกต้องของ logic ที่ใช้เขียนโปรแกรมว่าผลลัพธ์ที่ได้เป็นไปตามที่คาดหวังเพื่อให้โปรแกรมผิดพลาดน้อยที่สุดซึ่ง Maven ไม่ได้นิ่งเฉย ตรงกันข้ามมันถูกออกแบบให้รองรับสิ่งนี้ในระดับโครงสร้างของโปรเจกต์เทียวครับ

ความเดิมจาก part ที่แล้ว

พูดถึงการเขียนเทสคงไม่มีนักพัฒนาจาวาคนไหนไม่รู้จัก unit test tool อย่าง JUnit ซึ่งขณะนี้เดินทางมาถึงเวอร์ชันที่ 5 แล้ว แต่ก่อนจะไปจบที่ JUnit เรามาทำความรู้จักโครงสร้างที่ Maven เข้าใจเรื่องการเขียน unit test กันดีกว่า

Project Structure for Tests

https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html

จากภาพโครงสร้างที่ว่านั้นเริ่มจาก root project หรือก็คือ folder โปรเจกต์ hello (จาก part 1) ให้สร้าง sub folder ถัดจาก src เข้าไป ได้ว่า
src > test > java

แล้วจึงสร้างชื่อ package ต่อจากนั้น โดยทั่วไปแล้วชื่อ package ดังกล่าวก็จะเหมือนกับ groupId ที่ระบุไว้ใน pom.xml ได้ว่า
com.example.pros

ผมสร้างคลาสทดสอบชื่อ MainTests.java และลบ folder ชื่อ target ออกไปก่อน

package com.example.pros;public class MainTests {  void test1() {    System.out.println("Hello Tests");  }}

จากนั้นรันคำสั่ง

mvn package

ผล

ปรากฏไฟล์ MainTests.class อยู่ใน folder ชื่อ test-classes นั่นแสดงว่า Maven เข้าใจและแปลความถูกต้อง

Unit Test with JUnit 5

unit test tool ที่ผมเลือกใช้คือ JUnit 5 เป็น .jar ประกอบด้วยกลุ่มของ .class ที่นักพัฒนาได้เขียนและแจกจ่ายแก่ผู้ที่ต้องการนำไปใช้งาน โดยบอกกับ Maven ผ่าน pom.xml ในรูปแบบของ dependency

เปิดไฟล์ pom.xml แล้วเพิ่ม JUnit 5 dependency ขณะนี้เวอร์ชัน 5.6.2

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">...  <dependencies>    <dependency>      <groupId>org.junit.jupiter</groupId>      <artifactId>junit-jupiter</artifactId>      <version>5.6.2</version>      <scope>test</scope>    </dependency>  </dependencies></project>

เพื่อนๆจะสังเกตว่า groupId, artifactId และ version ได้เห็นผ่านตามาแล้วใน part 1 ที่เพิ่มเติมตอนนี้คือ scope

dependency scope หมายถึงการจำกัดขอบเขตใดๆของ dependency ตัวนั้นๆอันมีผลต่อกระบวนการ build tasks และการเปลี่ยนแปลงของ classpath ซึ่งจะกล่าวถึงอีกครั้งภายหลัง

จากตัวอย่างกำหนด depencency scope เป็น test นั่นหมายความว่า หากปราศจากกระบวนการที่จะไปเรียก phase ที่เกี่ยวข้องกับการ test ก็จะไม่สนใจโค้ดที่เขียน unit test เลย

พูดได้ว่า dependency scope เป็นการกำหนดบทบาทหน้าที่ตลอดจนศักดิ์ศรีของสามีและภรรยา (รวมเมียน้อยและชู้) ของทุกคนในบ้านให้แตกต่างกันออกไป บางงานก็จะสนใจและบางงานก็จะปล่อยไป หรือพูดว่าแบ่งกลุ่มภาระรับผิดชอบก็ได้

เมื่อระบุ dependency ใหม่เข้าไปหรือลบ dependency เดิมทิ้งไป เราจะต้องบอกกล่าวกับ Maven ทุกครั้ง (บาง IDE จะมี service ที่จะทำเรื่องนี้ให้อัติโนมัติ) เพื่อให้ dependency ทั้งหมด update เป็นปัจจุบันเสมอ

mvn dependency:resolve

ผล

คำสั่ง mvn dependency:resolve มีผลให้ Maven ทำ resolve all test scope (รวมถึง compile scope ด้วย) .jar ไหนไม่มีก็ดาวน์โหลดมา หรือ .jar ไหนไม่ใช้ก็นำออกจากโปรเจกต์ไป

มาถึงตรงนี้เชื่อว่าเพื่อนๆมือใหม่พอจะเห็นภาพของประดา .jar ที่ต้องคุยกันเป็นจำนวนมากและมันทั้งหมดต้องทำงานร่วมกันได้เป็นอย่างดี

กลับไปที่ MainTests.java เรามาเขียน test case เล็กๆกันโดยปรับปรุงโค้ดเป็น

package com.example.pros;import org.junit.jupiter.api.Test;import static org.junit.jupiter.api.Assertions.assertEquals;public class MainTests {  @Test  void test1() {    String iWantToSay = "Hello Tests";    assertEquals("Hello", iWantToSay);  }}

จากนั้นรัน

mvn package

ผล

BUILD SUCCESS แต่ว่า test case ไม่พัง มันเป็นเพราะอะไร?

เหตุผลนั้นมาจาก JUnit เวอร์ชัน 4 และ 5 ต่างกัน โดยเฉพาะเรื่องของ naming convention เบื้องต้นหมายความว่าคลาสทดสอบใดๆ (เช่น MainTests) จะต้องตั้งชื่อที่เลือกจากเงื่อนไขต่อไปนี้

ฉะนั้นผมเลือกใช้ JUnit 5 และลงท้ายคลาสทดสอบเป็น Tests จึงต้องแก้ไข pom.xml ให้ระบุ plugin ชื่อ maven-surefire-plugin เข้าไปด้วย

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">...  <dependencies>  ...  </dependencies>  <build>    <plugins>      <plugin>        <artifactId>maven-surefire-plugin</artifactId>        <version>2.22.2</version>      </plugin>    </plugins>  </build></project>

รัน

mvn package

ผล

มันต้องอย่างนี้สิ! บอกเลยว่าพังเพราะคาดหวังคำว่า “Hello” แต่กลับได้คำว่า “Hello Tests” มาแทน

ในเมื่อเราต้องการ say ว่า “Hello Tests” เราก็ไปแก้ไขความคาดหวังครับ

assertEquals("Hello Tests", iWantToSay);

แล้วจึงรันใหม่

ผล

สรุปตรงนี้ก่อนว่า Maven เข้าใจคลาสทดสอบได้ทั้งหมดตราบที่ตั้งชื่อตามกฏที่มันชอบและได้วางโครงสร้างของโปรเจกต์ตามที่มันกำหนดไว้

ขอส่งท้าย part นี้ด้วยคำสั่ง mvn test เฉยๆ

หากว่าเราลบ target folder ทิ้งไปแล้วรัน mvn test สิ่งที่ได้คือ target folder ใหม่ ของด้านในส่วนใหญ่เหมือนกับรัน mvn package ขาดก็แต่ไฟล์ .jar

โดยรวมจึงขอกล่าวถึง Maven ในส่วนของโครงสร้างสำหรับ test เพียงเท่านี้ ไว้พบกันใหม่ สวัสดีครับ

อ่านต่อ

อ้างอิง

https://maven.apache.org/plugins/maven-dependency-plugin/usage.html

https://www.baeldung.com/maven-dependency-scopes

https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html#Dependency_Scope

https://stackoverflow.com/questions/6178583/maven-does-not-find-junit-tests-to-run

--

--

No responses yet