Модульное тестирование Android Studio: чтение (входного) файла данных

96

Как в модульном тесте я могу прочитать данные из файла json в моей (настольной) файловой системе без жесткого кодирования пути?

Я хотел бы читать тестовый ввод (для моих методов синтаксического анализа) из файла вместо создания статических строк.

Файл находится в том же месте, что и мой код модульного тестирования, но при необходимости я могу разместить его в другом месте проекта. Я использую Android Studio.

Фрэнк
источник
3
Я пробовал почти все комбинации с IOUtils.toString (this.getClass (). GetResourceAsStream ("test_documents.json"), "UTF-8"), он всегда возвращает null. Вероятно, потому что файлы не попадут в банку.
Фрэнк
Мы говорим о модульных тестах с участием эмулятора / устройства Android?
AndroidEx
@ Android777 Я думаю, мы говорим о новой поддержке модульных тестов, представленной в последней версии Android Studio tools.android.com/tech-docs/unit-testing-support
akhy,
@ Фрэнк, где ты test_documents.json? каталог активов?
Ахы 06
Да, речь идет о новой поддержке модульных тестов, не связанных с эмулятором / устройством. Я не поместил его в каталог ресурсов, потому что тогда он будет упакован с живым apk. Я поместил его в ту же папку, что и тестовые (java) файлы.
Фрэнк

Ответы:

149

В зависимости от 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
Потрясающие! Ваше редактирование, без инструментальных тестов, отлично работает, спасибо!
Фрэнк
4
Хорошее решение, но работает не всегда. Если вы выполните задачу очистки, а затем запустите testDebug, она не удастся. В основном разработчик должен знать, что он должен запустить задачу AssemblyDebug перед testDebug. Есть ли у вас какие-нибудь предложения по улучшению этого?
joaoprudencio
18
В AndroidStudio 1.5 с плагином android 1.5.0 разместите здесь свой json-файл src/test/resources/test.jsonи укажите на него ссылку classLoader.getResource("test.json"). Никакой модификации gradle не требуется. Вложенные папки внутри resourcesтоже работают.
Сергей Печеницкий
6
Неполный ответ. Что есть classLoader? где разместить эту строчку кода? что вы можете сделать с возвращаемым результатом ?. Пожалуйста, предоставьте более полный код. -1
voghDev
3
Читать ./src/test/resources/file.txtв Котлине:TestClassName::class.java.getResource("/file.txt")!!.readText()
Erik
76

Для локальных модульных тестов (в src/test/resourcesотличие от инструментальных) вы можете размещать файлы и читать их с помощью classLoader. Например, следующий код открывает myFile.txtфайл в каталоге ресурсов.

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

Он работал с

  • Android Studio 1.5.1
  • плагин Gradle 1.3.1
user3113045
источник
Это не работает для меня, пожалуйста, посмотрите мой вопрос здесь stackoverflow.com/questions/36295824/…
Тайлер Пфафф
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 в этом случае дает нулевой результат.
Ананд Криш
4
котлин:val myFile = ClassLoader.getSystemResource("myFile.txt").readText()
jj.
Работал как шарм!
Амит Бхандари,
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 Studio, но они передадут командную строку с ./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
Ницца! Я вижу здесь ту же проблему с загрузкой ресурсов. AS 2.3.2, Gradle 3.3. Тесты работают из командной строки, но не через AS, поскольку они build/resources/testне включены в интерактивный путь к классам.
Марк МакКенна
6

Если вы перейдете в Run -> Edit configurations -> JUnit, а затем выберите конфигурацию запуска для своих модульных тестов, появится параметр «Рабочий каталог». Это должно указывать на то, где находится ваш файл json. Имейте в виду, что это может нарушить другие тесты.

Тас Морф
источник
а чем использовать this.getClass (). getResourceAsStream (test.json)? Добавил туда файлы, в корень проекта, все равно файлы не находит.
Фрэнк
Его можно использовать для загрузки файлов с помощью new File()или аналогичного, но он не работает напрямую с методом загрузки пути к классам, описанным выше. Это также немного сложно, потому что каждая новая конфигурация запуска должна устанавливать рабочий каталог, и по умолчанию AS и командная строка Gradle любят использовать разные рабочие каталоги ... такая боль
Марк МакКенна
5

Я подумал, что должен добавить сюда свои выводы. Я знаю, что это немного устарело, но для более новых версий Gradle, где нет каталога src / test / resources, а есть только один каталог ресурсов для всего проекта, вам нужно добавить эту строку в свой файл Gradle.

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

Сделав это, вы можете получить доступ к своему ресурсу с помощью:

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

Я искал это и не мог найти ответа, поэтому решил помочь другим здесь.

Рафаэль Эйрес
источник
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");

Мишель
источник