การทดสอบหน่วย Android Studio: อ่านไฟล์ข้อมูล (อินพุต)


99

ในการทดสอบหน่วยฉันจะอ่านข้อมูลจากไฟล์ json บนระบบไฟล์ (เดสก์ท็อป) ของฉันโดยไม่เข้ารหัสพา ธ ได้อย่างไร

ฉันต้องการอ่านอินพุตทดสอบ (สำหรับวิธีการแยกวิเคราะห์ของฉัน) จากไฟล์แทนการสร้างสตริงแบบคงที่

ไฟล์อยู่ในตำแหน่งเดียวกับรหัสการทดสอบหน่วยของฉัน แต่ฉันสามารถวางไว้ที่อื่นในโครงการได้หากจำเป็น ฉันใช้ Android Studio


3
ฉันลองเกือบทุกชุดร่วมกับ IOUtils.toString (this.getClass (). getResourceAsStream ("test_documents.json"), "UTF-8") มันจะคืนค่า null เสมอ อาจเป็นเพราะไฟล์จะไม่รวมอยู่ใน jar
— Frank

เรากำลังพูดถึงการทดสอบหน่วยที่เกี่ยวข้องกับโปรแกรมจำลอง / อุปกรณ์ Android หรือไม่?
— AndroidEx

@ Android777 ฉันคิดว่าเรากำลังพูดถึงการสนับสนุนการทดสอบหน่วยใหม่ที่เปิดตัวใน Android Studio tools.android.com/tech-docs/unit-testing-support
— akhy

@ แฟรงค์คุณวางtest_documents.jsonที่ไหน? ไดเรกทอรีทรัพย์สิน?
— akhy

ใช่เรากำลังพูดถึงการสนับสนุนการทดสอบหน่วยใหม่ซึ่งไม่เกี่ยวข้องกับโปรแกรมจำลอง / อุปกรณ์ ฉันไม่ได้วางไว้ใน dir สินทรัพย์เพราะมันจะได้รับการบรรจุด้วย apk สด ฉันวางไว้ในโฟลเดอร์เดียวกับไฟล์ทดสอบ (java)
— Frank

คำตอบ:


152

ขึ้นอยู่กับandroid-gradle-pluginรุ่น:

1. เวอร์ชัน 1.5 และสูงกว่า:

เพียงใส่ไฟล์ json src/test/resources/test.jsonและอ้างอิงเป็นไฟล์

classLoader.getResource("test.json"). 

ไม่จำเป็นต้องมีการปรับเปลี่ยน gradle

2. เวอร์ชันต่ำกว่า 1.5 : (หรือด้วยเหตุผลบางประการวิธีแก้ปัญหาข้างต้นไม่ทำงาน)

  1. ตรวจสอบให้แน่ใจว่าคุณใช้Android Gradle Plugin เวอร์ชัน 1.1 เป็นอย่างน้อย ตามลิงค์เพื่อตั้งค่า Android Studio ให้ถูกต้อง

  2. สร้างtestไดเร็กทอรี ใส่คลาสทดสอบหน่วยในjavaไดเร็กทอรีและใส่ไฟล์ทรัพยากรของคุณในresไดเร็กทอรี Android Studio ควรทำเครื่องหมายเป็นดังนี้:

    ป้อนคำอธิบายภาพที่นี่

  3. สร้างgradleงานเพื่อคัดลอกรีซอร์สไปยังไดเร็กทอรีคลาสเพื่อให้มองเห็นได้สำหรับclassloader:

    android{
       ...
    }
    
    task copyResDirectoryToClasses(type: Copy){
        from "${projectDir}/src/test/res"
        into "${buildDir}/intermediates/classes/test/debug/res"
    }
    
    assembleDebug.dependsOn(copyResDirectoryToClasses)
    
  4. ตอนนี้คุณสามารถใช้วิธีนี้เพื่อรับFileข้อมูลอ้างอิงสำหรับทรัพยากรไฟล์:

    private static File getFileFromPath(Object obj, String fileName) {
        ClassLoader classLoader = obj.getClass().getClassLoader();
        URL resource = classLoader.getResource(fileName);
        return new File(resource.getPath());
    }
    
    @Test
    public void fileObjectShouldNotBeNull() throws Exception {
        File file = getFileFromPath(this, "res/test.json");
        assertThat(file, notNullValue());
    }
    
  5. เรียกใช้การทดสอบหน่วยโดยCtrl+ Shift+ F10ในทั้งชั้นเรียนหรือวิธีการทดสอบเฉพาะ

1
สุดยอด! การแก้ไขของคุณโดยไม่มีการทดสอบเครื่องมือใช้งานได้อย่างมีเสน่ห์ขอบคุณ!
— Frank

4
เป็นทางออกที่ดี แต่ก็ไม่ได้ผลเสมอไป หากคุณรันงานใหม่ทั้งหมดแล้วรัน testDebug จะล้มเหลว โดยพื้นฐานแล้วนักพัฒนาจำเป็นต้องรู้ว่าเขาต้องรันงาน AssembleDebug ก่อน testDebug พวกคุณมีข้อเสนอแนะในการปรับปรุงสิ่งนี้หรือไม่?
— joaoprudencio

19
ด้วย AndroidStudio 1.5 พร้อมปลั๊กอิน Android 1.5.0 วางไฟล์ json ของคุณที่นี่src/test/resources/test.jsonและอ้างอิงเป็นclassLoader.getResource("test.json")ไฟล์. ไม่จำเป็นต้องมีการปรับเปลี่ยน gradle โฟลเดอร์ที่ซ้อนกันภายในresourcesก็ใช้งานได้เช่นกัน
— Sergii Pechenizkyi

6
คำตอบที่ไม่สมบูรณ์ คืออะไรclassLoader? จะวางโค้ดบรรทัดนี้ไว้ที่ไหน คุณสามารถทำอะไรกับผลลัพธ์ที่ได้?. โปรดระบุรหัสที่สมบูรณ์ยิ่งขึ้น -1
— voghDev

3
อ่าน./src/test/resources/file.txtใน Kotlin:TestClassName::class.java.getResource("/file.txt")!!.readText()
— Erik

79

สำหรับการทดสอบหน่วยในพื้นที่ (เทียบกับการทดสอบเครื่องมือวัด) คุณสามารถวางไฟล์ไว้ข้างใต้src/test/resourcesและอ่านโดยใช้ classLoader ตัวอย่างเช่นโค้ดต่อไปนี้จะเปิดmyFile.txtไฟล์ในไดเร็กทอรีทรัพยากร

InputStream in = this.getClass().getClassLoader().getResourceAsStream("myFile.txt");

มันทำงานร่วมกับ

  • Android Studio 1.5.1
  • ปลั๊กอิน gradle 1.3.1

สิ่งนี้ไม่ได้ผลสำหรับฉันโปรดดูคำถามของฉันที่นี่stackoverflow.com/questions/36295824/…
— Tyler Pfaff

5
สิ่งนี้ใช้ได้ผลสำหรับฉันเมื่อฉันเปลี่ยนจาก "src / test / res" เป็น "src / test / resources" ใช้ Android Studio 2.0 RC 3, gradle: 2.0.0-rc3
— Alex Bravo

ทำงานได้ตามที่คาดไว้ แต่การทดสอบจะผ่านไปแม้ว่าไฟล์จะไม่มีประโยชน์ภายใต้ "src / test / resources /" เช่น: "rules.xml" แต่ InputStream จะทำให้ผลลัพธ์เป็นโมฆะในกรณีนี้
— anand krish

4
ก๊อตลิน:val myFile = ClassLoader.getSystemResource("myFile.txt").readText()
— jj.

ทำงานอย่างมีเสน่ห์!
— Amit Bhandari

15

ในกรณีของฉันวิธีแก้ปัญหาคือเพิ่มลงในไฟล์ gradle

sourceSets {
    test.resources.srcDirs += 'src/unitTests/resources'
  } 

หลังจากนั้นทุกอย่างก็พบโดย AS 2.3.1

javaClass.classLoader.getResourceAsStream("countries.txt")

1
ฉันทำสิ่งที่คล้ายกันมาก (Kotlin): 1. สร้างโฟลเดอร์และไฟล์ที่: root/app/src/test/resources/test.json 2. อ่านข้อมูลดังนี้:val jsonFile = ClassLoader.getSystemResource("test.json").readText()
— jj.

8

ฉันมีปัญหามากมายเกี่ยวกับแหล่งข้อมูลการทดสอบใน Android Studio ดังนั้นฉันจึงตั้งค่าการทดสอบบางอย่างเพื่อความชัดเจน ใน mobileโครงการ (แอปพลิเคชัน Android) ของฉันฉันได้เพิ่มไฟล์ต่อไปนี้:

mobile/src/test/java/test/ResourceTest.java
mobile/src/test/resources/test.txt
mobile/src/test/resources/test/samePackage.txt

ชั้นทดสอบ (การทดสอบทั้งหมดผ่าน):

assertTrue(getClass().getResource("test.txt") == null);
assertTrue(getClass().getResource("/test.txt").getPath().endsWith("test.txt"));
assertTrue(getClass().getResource("samePackage.txt").getPath().endsWith("test/samePackage.txt"));
assertTrue(getClass().getResource("/test/samePackage.txt").getPath().endsWith("test/samePackage.txt"));
assertTrue(getClass().getClassLoader().getResource("test.txt").getPath().endsWith("test.txt"));
assertTrue(getClass().getClassLoader().getResource("test/samePackage.txt").getPath().endsWith("test/samePackage.txt"));

ในโครงการรากเดียวกันฉันมีJava (ไม่ใช่ Android) โครงการdataที่เรียกว่า หากฉันเพิ่มไฟล์เดียวกันในโปรเจ็กต์ข้อมูล:

data/src/test/java/test/ResourceTest.java
data/src/test/resources/test.txt
data/src/test/resources/test/samePackage.txt

จากนั้นการทดสอบทั้งหมดดังกล่าวข้างต้นจะล้มเหลวถ้าผมดำเนินการได้จาก Android สตูดิโอ, ./gradlew data:testแต่พวกเขาผ่านในบรรทัดคำสั่งด้วย เพื่อหลีกเลี่ยงมันฉันใช้แฮ็คนี้ (ใน Groovy)

def resource(String path) {
    getClass().getResource(path) ?:
            // Hack to load test resources when executing tests from Android Studio
            new File(getClass().getClassLoader().getResource('.').path
                    .replace('/build/classes/test/', "/build/resources/test$path"))
}

การใช้งาน: resource('/test.txt')

Android Studio 2.3, Gradle 3.3


คำตอบที่ดี (หลังจากค้นหา 45 นาที😌) เป็นหัวใจสำคัญของปัญหาต่างๆและทำให้ง่ายต่อการจำลองผลลัพธ์โดยใช้การทดสอบด้วยตนเอง Fwiw ใน AS 2.3.1, Gradle 2.3.1 getClassLoader()เวอร์ชันทำงานได้ตามที่คาดไว้ กล่าวคือพวกเขาค้นหาไฟล์ทั้งสองทำงานจาก Android Studio และจากบรรทัดคำสั่งบนเครื่อง macOS ของฉันและบนเซิร์ฟเวอร์ Linux CI (CircleCI) ฉันจะทำอย่างนั้นและหวังว่า Gradle 3.3 จะไม่พังในภายหลัง ...
— mm2001

ดี! ฉันพบปัญหาเดียวกันในการโหลดทรัพยากรที่นี่ เป็น 2.3.2, Gradle 3.3 การทดสอบทำงานจากบรรทัดคำสั่ง แต่ไม่ผ่าน AS เนื่องจากbuild/resources/testไม่รวมอยู่ในคลาสพา ธ แบบโต้ตอบ
— Mark McKenna

6

หากคุณไปที่ Run -> Edit configuration -> JUnit จากนั้นเลือก run configuration สำหรับการทดสอบยูนิตของคุณจะมีการตั้งค่า 'Working directory' ที่ควรชี้ไปที่ใดก็ตามที่ไฟล์ json ของคุณอยู่ โปรดทราบว่าอาจทำให้การทดสอบอื่น ๆ เสียหายได้


และใช้ this.getClass (). getResourceAsStream (test.json)? ฉันเพิ่มไฟล์ที่นั่นในรูทของโปรเจ็กต์ แต่ก็ยังไม่พบไฟล์
— Frank

สามารถใช้เพื่อโหลดไฟล์โดยใช้new File()หรือคล้ายกัน แต่ใช้ไม่ได้โดยตรงกับวิธีการโหลด classpath ที่อธิบายไว้ข้างต้น นอกจากนี้ยังค่อนข้างยุ่งยากเนื่องจากการกำหนดค่าการรันใหม่แต่ละครั้งจำเป็นต้องตั้งค่า dir ที่ใช้งานได้และโดยค่าเริ่มต้น AS และ Gradle command line ต้องการใช้ dirs การทำงานที่แตกต่างกัน ... เจ็บปวดเช่นนี้
— Mark McKenna

6

ฉันควรเพิ่มสิ่งที่ค้นพบที่นี่ ฉันรู้ว่านี่เก่าไปหน่อย แต่สำหรับ Gradle เวอร์ชันที่ใหม่กว่าซึ่งมีไดเร็กทอรี NO src / test / resources แต่มีไดเร็กทอรีทรัพยากรเดียวสำหรับโปรเจ็กต์ทั้งหมดคุณต้องเพิ่มบรรทัดนี้ลงในไฟล์ Gradle ของคุณ

android {
   testOptions {
      unitTests {
         includeAndroidResources = true
      }
    }
}

โดยการดำเนินการนี้คุณสามารถเข้าถึงทรัพยากรของคุณด้วย:

 this.getClass().getClassLoader().getResourceAsStream(fileName);

ฉันค้นหาสิ่งนี้มาตลอด แต่ไม่สามารถหาคำตอบได้ดังนั้นฉันจึงตัดสินใจช่วยเหลือผู้อื่นที่นี่


ขอบคุณ Raphael Ayres นี่ควรเป็นความคิดเห็นที่ได้รับการโหวตสูงสุด นอกจากนี้คุณต้องสร้างไดเรกทอรีทรัพยากรของคุณภายใต้ src / test / ดังนั้นคุณจะมี src / test / resources / ...
— Martin Nowosad

2

อันที่จริงนี่คือสิ่งที่ใช้ได้ผลสำหรับฉันกับการทดสอบเครื่องมือ (ใช้ Android Studio เวอร์ชัน3.5.3 , Android Gradle Plugin เวอร์ชัน3.5.3 , Gradle เวอร์ชัน6.0.1 ):

  1. ใส่ไฟล์ของคุณในsrc/androidTest/assetsโฟลเดอร์
  2. ในไฟล์ทดสอบของคุณ:

InputStream is = InstrumentationRegistry.getInstrumentation().getContext().getAssets().open("filename.txt");

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.