การทดสอบหน่วย 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.