ฉันกำลังทำงานกับโปรเจ็กต์ java ฉันยังใหม่กับการทดสอบหน่วย เป็นวิธีที่ดีที่สุดในการทดสอบหน่วยวิธีส่วนตัวในชั้นเรียน java อะไร
ฉันกำลังทำงานกับโปรเจ็กต์ java ฉันยังใหม่กับการทดสอบหน่วย เป็นวิธีที่ดีที่สุดในการทดสอบหน่วยวิธีส่วนตัวในชั้นเรียน java อะไร
คำตอบ:
โดยทั่วไปคุณไม่ได้ทดสอบหน่วยวิธีการส่วนตัวโดยตรง เนื่องจากเป็นข้อมูลส่วนบุคคลให้พิจารณารายละเอียดการใช้งาน ไม่มีใครเคยเรียกหนึ่งในพวกเขาและคาดหวังว่ามันจะทำงานในรูปแบบเฉพาะ
คุณควรทดสอบส่วนต่อประสานสาธารณะแทน หากวิธีการที่เรียกวิธีการส่วนตัวของคุณทำงานอย่างที่คุณคาดไว้คุณจะถือว่าส่วนขยายนั้นวิธีส่วนตัวของคุณทำงานอย่างถูกต้อง
โดยทั่วไปแล้วฉันจะหลีกเลี่ยง หากวิธีการส่วนตัวของคุณซับซ้อนมากจนต้องใช้การทดสอบหน่วยแยกต่างหากก็มักจะหมายความว่าสมควรได้รับคลาสของตัวเอง สิ่งนี้อาจกระตุ้นให้คุณเขียนด้วยวิธีที่สามารถใช้ซ้ำได้ จากนั้นคุณควรทดสอบคลาสใหม่และเรียกใช้อินเทอร์เฟซสาธารณะในคลาสเก่าของคุณ
ในทางกลับกันบางครั้งการแยกแยะรายละเอียดการใช้งานออกเป็นคลาสที่แยกจากกันจะนำไปสู่คลาสที่มีอินเตอร์เฟสที่ซับซ้อนข้อมูลจำนวนมากที่ส่งผ่านระหว่างคลาสเก่าและคลาสใหม่หรือการออกแบบที่อาจดูดีจากมุมมองของ OOP จับคู่กับสัญชาติญาณที่มาจากโดเมนปัญหา (เช่นการแยกโมเดลราคาออกเป็นสองส่วนเพื่อหลีกเลี่ยงการทดสอบวิธีการส่วนตัวนั้นไม่ง่ายนักและอาจนำไปสู่ปัญหาในภายหลังเมื่อทำการบำรุงรักษา / ขยายรหัส) คุณไม่ต้องการที่จะมี "คลาสคู่" ซึ่งเปลี่ยนไปด้วยกันเสมอ
เมื่อต้องเผชิญกับทางเลือกระหว่างการห่อหุ้มและการทดสอบฉันต้องการไปที่สอง สิ่งสำคัญคือต้องมีรหัสที่ถูกต้อง (เช่นสร้างผลลัพธ์ที่ถูกต้อง) กว่าการออกแบบ OOP ที่ดีซึ่งทำงานไม่ถูกต้องเพราะมันไม่ได้ทดสอบอย่างเพียงพอ ใน Java คุณสามารถให้วิธีการ "เริ่มต้น" การเข้าถึงและทดสอบหน่วยในแพคเกจเดียวกัน การทดสอบหน่วยเป็นเพียงส่วนหนึ่งของแพคเกจที่คุณกำลังพัฒนาและมันก็โอเคที่จะมีการพึ่งพาระหว่างการทดสอบและรหัสที่จะถูกทดสอบ หมายความว่าเมื่อคุณเปลี่ยนการใช้งานคุณอาจต้องเปลี่ยนการทดสอบของคุณ แต่ก็ไม่เป็นไร - การเปลี่ยนแปลงของการติดตั้งแต่ละครั้งนั้นจำเป็นต้องทำการทดสอบรหัสอีกครั้งและหากการทดสอบนั้นจำเป็นต้องแก้ไขเพื่อให้ทำเช่นนั้นคุณก็ทำได้ มัน.
โดยทั่วไปคลาสอาจเสนอมากกว่าหนึ่งอินเตอร์เฟส มีส่วนต่อประสานสำหรับผู้ใช้และส่วนต่อประสานสำหรับผู้ดูแล คนที่สองสามารถเปิดเผยเพิ่มเติมเพื่อให้แน่ใจว่ารหัสมีการทดสอบอย่างเพียงพอ มันไม่จำเป็นต้องเป็นการทดสอบหน่วยในวิธีการแบบส่วนตัว - ตัวอย่างเช่นการบันทึก การบันทึกยัง "หยุดการห่อหุ้ม" แต่เรายังคงทำเพราะมันมีประโยชน์มาก
การทดสอบวิธีการส่วนตัวจะขึ้นอยู่กับความซับซ้อนของพวกเขา วิธีการส่วนตัวแบบหนึ่งบรรทัดจะไม่รับประกันความพยายามพิเศษของการทดสอบ (ซึ่งอาจกล่าวได้ว่าวิธีการแบบสาธารณะ) แต่วิธีการแบบส่วนตัวบางอย่างอาจซับซ้อนพอ ๆ กับวิธีการแบบสาธารณะและยากที่จะทดสอบผ่านส่วนต่อประสานสาธารณะ
เทคนิคที่ฉันชอบคือการทำให้แพคเกจวิธีส่วนตัวเป็นส่วนตัวซึ่งจะอนุญาตให้เข้าถึงการทดสอบหน่วยในแพคเกจเดียวกัน แต่มันจะยังคงถูกห่อหุ้มจากรหัสอื่น ๆ ทั้งหมด สิ่งนี้จะให้ประโยชน์จากการทดสอบตรรกะวิธีส่วนตัวโดยตรงแทนที่จะต้องอาศัยการทดสอบวิธีสาธารณะเพื่อครอบคลุมทุกส่วนของตรรกะที่ซับซ้อน (อาจ)
หากสิ่งนี้จับคู่กับคำอธิบายประกอบ @VisibleForTesting ในห้องสมุด Google Guava คุณกำลังทำเครื่องหมายวิธีการส่วนตัวของแพคเกจนี้อย่างชัดเจนว่ามองเห็นได้สำหรับการทดสอบเท่านั้นและไม่ควรเรียกชั้นเรียนอื่น ๆ
ฝ่ายตรงข้ามของเทคนิคนี้ยืนยันว่าสิ่งนี้จะทำลาย encapsulation และเปิดวิธีการส่วนตัวเพื่อรหัสในแพคเกจเดียวกัน ในขณะที่ฉันยอมรับว่าการแบ่ง encapsulation และเปิดรหัสส่วนตัวไปยังคลาสอื่นฉันยืนยันว่าการทดสอบตรรกะที่ซับซ้อนมีความสำคัญมากกว่าการห่อหุ้มที่เข้มงวดและไม่ใช้วิธีการส่วนตัวของแพคเกจที่มีการทำเครื่องหมายอย่างชัดเจนว่ามองเห็นได้สำหรับการทดสอบเท่านั้น การใช้และการเปลี่ยนรหัสฐาน
วิธีการส่วนตัวก่อนทดสอบ:
private int add(int a, int b){
return a + b;
}
แพ็คเกจส่วนตัวพร้อมสำหรับการทดสอบ:
@VisibleForTesting
int add(int a, int b){
return a + b;
}
หมายเหตุ: การวางการทดสอบในแพ็คเกจเดียวกันไม่เท่ากับการทดสอบในโฟลเดอร์ทางกายภาพเดียวกัน การแยกรหัสหลักและรหัสทดสอบของคุณออกเป็นโครงสร้างโฟลเดอร์แบบฟิสิคัลแยกเป็นแนวปฏิบัติที่ดีโดยทั่วไป แต่เทคนิคนี้จะทำงานได้ตราบใดที่คลาสมีการกำหนดไว้ในแพ็คเกจเดียวกัน
หากคุณไม่สามารถใช้ API ภายนอกหรือไม่ต้องการคุณยังคงสามารถใช้ JDK API มาตรฐานเพื่อเข้าถึงวิธีการส่วนตัวโดยใช้การสะท้อนกลับ นี่คือตัวอย่าง
MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);
ตรวจสอบ Java Tutorial http://docs.oracle.com/javase/tutorial/reflect/หรือ Java API http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary htmlสำหรับข้อมูลเพิ่มเติม
ตามที่ @ kij อ้างถึงคำตอบของเขามีหลายครั้งที่วิธีง่ายๆในการใช้การสะท้อนนั้นดีมากในการทดสอบวิธีการส่วนตัว
กรณีทดสอบหน่วยหมายถึงการทดสอบหน่วยของรหัส ไม่ได้หมายถึงการทดสอบอินเตอร์เฟสเพราะถ้าคุณกำลังทดสอบอินเตอร์เฟสนั่นไม่ได้หมายความว่าคุณกำลังทดสอบหน่วยของรหัส มันกลายเป็นการทดสอบกล่องดำ นอกจากนี้ยังเป็นการดีกว่าที่จะค้นหาปัญหาในระดับหน่วยที่เล็กที่สุดกว่าการกำหนดปัญหาในระดับอินเทอร์เฟซแล้วลองดีบั๊กว่าส่วนใดไม่ทำงาน ดังนั้นกรณีทดสอบหน่วยควรทดสอบโดยไม่คำนึงถึงขอบเขต ต่อไปนี้เป็นวิธีการทดสอบวิธีการส่วนตัว
หากคุณใช้จาวาคุณสามารถใช้ jmockit ซึ่งให้ Deencapsulation.invoke เพื่อเรียกใช้เมธอดส่วนตัวของคลาสภายใต้การทดสอบ มันใช้การสะท้อนที่เรียกว่าในที่สุด แต่ให้เสื้อคลุมที่ดีรอบ ( https://code.google.com/p/jmockit/ )
ก่อนอื่นตามที่ผู้เขียนคนอื่นแนะนำ: คิดสองครั้งถ้าคุณจำเป็นต้องทดสอบวิธีการส่วนตัว และถ้าเป็นเช่นนั้น ...
ใน. NET คุณสามารถแปลงเป็นวิธีการ "ภายใน" และทำให้แพคเกจ " InternalVisible " กับโครงการทดสอบหน่วยของคุณ
ใน Java คุณสามารถเขียนแบบทดสอบในชั้นเรียนเพื่อทดสอบและวิธีการทดสอบของคุณควรจะเรียกวิธีส่วนตัวเช่นกัน ฉันไม่มีประสบการณ์ Java ขนาดใหญ่จริงๆดังนั้นนั่นอาจไม่ใช่วิธีปฏิบัติที่ดีที่สุด
ขอบคุณ
ฉันมักจะทำให้วิธีการดังกล่าวได้รับการคุ้มครอง สมมติว่าคลาสของคุณอยู่ใน:
src/main/java/you/Class.java
คุณสามารถสร้างคลาสการทดสอบเป็น:
src/test/java/you/TestClass.java
ตอนนี้คุณสามารถเข้าถึงวิธีที่ได้รับการป้องกันและสามารถทดสอบหน่วยเหล่านั้นได้ (JUnit หรือ TestNG ไม่สำคัญเลย) แต่คุณยังคงวิธีการเหล่านี้จากผู้โทรที่คุณไม่ต้องการ
โปรดทราบว่าสิ่งนี้คาดว่าจะมีต้นกำเนิดของสไตล์ maven
หากคุณต้องการจริงๆที่จะทดสอบวิธีการส่วนตัวกับ Java ฉันหมายความว่าคุณสามารถใช้และfest assert
/ หรือ fest reflect
มันใช้การสะท้อน
นำเข้าไลบรารีด้วย maven (เวอร์ชันที่ระบุไม่ใช่เวอร์ชันล่าสุดที่ฉันคิด) หรือนำเข้าโดยตรงใน classpath ของคุณ:
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.2</version>
</dependency>
ตัวอย่างเช่นถ้าคุณมีคลาสชื่อ 'MyClass' ด้วยเมธอดส่วนตัวที่ชื่อว่า 'myPrivateMethod' ซึ่งใช้สตริงเป็นพารามิเตอร์ให้อัปเดตค่าเป็น 'นี่คือการทดสอบที่ยอดเยี่ยม!' คุณสามารถทำการทดสอบ Junit ต่อไปนี้:
import static org.fest.reflect.core.Reflection.method;
...
MyClass objectToTest;
@Before
public void setUp(){
objectToTest = new MyClass();
}
@Test
public void testPrivateMethod(){
// GIVEN
String myOriginalString = "toto";
// WHEN
method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
// THEN
Assert.assertEquals("this is cool testing !", myOriginalString);
}
ไลบรารีนี้ยังช่วยให้คุณสามารถแทนที่คุณสมบัติ bean ใด ๆ (ไม่ว่าจะเป็นแบบส่วนตัวและไม่มีการเขียนตัวตั้งค่า) โดยจำลองและใช้สิ่งนี้กับ Mockito หรือกรอบงานจำลองอื่น ๆ ก็เจ๋งจริงๆ สิ่งเดียวที่คุณต้องรู้ในขณะนี้ (ไม่ทราบว่าจะดีกว่าในรุ่นถัดไป) คือชื่อของเขตข้อมูล / วิธีการที่คุณต้องการจัดการและลายเซ็นของมัน
สิ่งที่ฉันทำตามปกติใน C # คือทำให้วิธีการของฉันได้รับการป้องกันและไม่เป็นส่วนตัว มันเป็นโมดิฟายเออร์ส่วนตัวที่เข้าถึงได้น้อยกว่าเล็กน้อย แต่มันซ่อนเมธอดจากคลาสทั้งหมดที่ไม่สืบทอดจากคลาสที่อยู่ภายใต้การทดสอบ
public class classUnderTest
{
//this would normally be private
protected void methodToTest()
{
//some code here
}
}
คลาสใดที่ไม่สืบทอดโดยตรงจาก classUnderTest ไม่มีความคิดที่ว่า methodToTest ยังคงมีอยู่ ในรหัสทดสอบของฉันฉันสามารถสร้างคลาสการทดสอบพิเศษที่ขยายและให้การเข้าถึงวิธีนี้ ...
class TestingClass : classUnderTest
{
public void methodToTest()
{
//this returns the method i would like to test
return base.methodToTest();
}
}
คลาสนี้มีอยู่ในโครงการทดสอบของฉันเท่านั้น มีวัตถุประสงค์เพียงอย่างเดียวคือให้การเข้าถึงวิธีการเดียวนี้ อนุญาตให้ฉันเข้าถึงสถานที่ที่คลาสอื่นส่วนใหญ่ไม่มี
protected
เป็นส่วนหนึ่งของ API สาธารณะและเกิดข้อ จำกัด เดียวกัน (ไม่สามารถเปลี่ยนแปลงได้จะต้องทำเป็นเอกสาร, ... ) ใน C # คุณสามารถใช้ร่วมกับinternal
InternalsVisibleTo
คุณสามารถทดสอบวิธีการส่วนตัวได้อย่างง่ายดายหากคุณทำการทดสอบหน่วยในชั้นเรียนในชั้นเรียนที่คุณกำลังทดสอบ การใช้ TestNG การทดสอบหน่วยของคุณจะต้องเป็นคลาสภายในสแตติกสาธารณะที่มีคำอธิบายประกอบด้วย @Test เช่นนี้:
@Test
public static class UserEditorTest {
public void test_private_method() {
assertEquals(new UserEditor().somePrivateMethod(), "success");
}
}
เนื่องจากเป็นชั้นในจึงสามารถเรียกใช้วิธีส่วนตัวได้
การทดสอบของฉันถูกเรียกใช้จาก Maven และจะค้นหากรณีทดสอบเหล่านี้โดยอัตโนมัติ ถ้าคุณแค่ต้องการทดสอบหนึ่งคลาสคุณสามารถทำได้
$ mvn test -Dtest=*UserEditorTest
ที่มา: http://www.ninthavenue.com.au/how-to-unit-test-private-methods