Jak czytać plik tekstowy z zasobów w Kotlinie?

97

Chcę napisać test Speka w Kotlinie. Test powinien odczytać plik HTML z src/test/resourcesfolderu. Jak to zrobić?

class MySpec : Spek({

    describe("blah blah") {

        given("blah blah") {

            var fileContent : String = ""

            beforeEachTest {
                // How to read the file file.html in src/test/resources/html
                fileContent = ...  
            }

            it("should blah blah") {
                ...
            }
        }
    }
})
Olaf
źródło

Odpowiedzi:

116
val fileContent = MySpec::class.java.getResource("/html/file.html").readText()
JB Nizet
źródło
30
Dla mnie to nie zadziałało, musiałem to zmienić nathis::class.java.classLoader.getResource("/html/file.html").readText()
pavlos163
4
U mnie obie te opcje działały w aplikacji na Androida (zwróć uwagę na dodatek /w jednej z nich, który trzeba usunąć w drugiej): this::class.java.getResource("/html/file.html").readText()ithis::class.java.classLoader.getResource("html/file.html").‌​readText()
Franco
21
val fileContent = javaClass.getResource("/html/file.html").readText()wykonuje pracę jeszcze krócej
Frank Neblung,
1
Co dziwne, zawsze potrzebuję wiodącego cięcia. Np. Jeśli plik znajduje się w katalogu głównym zasobów, nadal musisz odnosić się do niego jako „/file.html”
Somaiah Kumbera
28

inne nieco inne rozwiązanie:

@Test
fun basicTest() {
    "/html/file.html".asResource {
        // test on `it` here...
        println(it)
    }

}

fun String.asResource(work: (String) -> Unit) {
    val content = this.javaClass::class.java.getResource(this).readText()
    work(content)
}
jhodges
źródło
Niezłe użycie funkcji rozszerzenia! Ale dlaczego używasz tutaj funkcji lambda? Nie ma to dla mnie większego sensu. Ponadto thisczęść nie działała dla mnie. Dlatego polecam:fun String.asResource(): URL? = object {}.javaClass.getResource(this)
Qw3ry
Podoba mi się ta metoda pracy, w której deklarujesz plik, a następnie pracujesz nad zawartością tego pliku. Chyba osobiste preferencje. thisw powyższym przykładzie odnosi się do obiektu ciągu.
jhodges
to straszne nadużycie funkcji rozszerzenia. Ładowanie plików nie dotyczy klasy String.
Ben
jest w tym kontekście. nie udostępniłbym tego globalnie ani nie używałbym go poza klasami testowymi. Uważam to za bardziej funkcję mapującą.
jhodges
28

Nie mam pojęcia, dlaczego jest to takie trudne, ale najprostszy sposób, jaki znalazłem (bez konieczności odwoływania się do konkretnej klasy), to:

fun getResourceAsText(path: String): String {
    return object {}.javaClass.getResource(path).readText()
}

A następnie przekazując bezwzględny adres URL, np

val html = getResourceAsText("/www/index.html")
Russell Briggs
źródło
jest {}wymagane? Dlaczego nie po prostu javaClass.getResource(path).readText()?
andrewgazelka
2
javaClass musi zostać wywołana na obiekcie zgodnie z dokumentacją kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html Jeśli to działa bez, to idź na całe życie :)
Russell Briggs
3
Wadą powyższej metody jest to, że tworzy nowy obiekt dla każdego dostępu do zasobów. Byłoby lepiej przechowywać fikcyjny obiekt poza funkcją.
Russell Briggs
@RussellBriggs tbh Myślę, że to nie ma większego znaczenia. Wydajność tworzenia obiektów nie jest tak naprawdę problemem, jeśli masz dostęp do dysku!
Qw3ry
13

Nieco inne rozwiązanie:

class MySpec : Spek({
    describe("blah blah") {
        given("blah blah") {

            var fileContent = ""

            beforeEachTest {
                html = this.javaClass.getResource("/html/file.html").readText()
            }

            it("should blah blah") {
                ...
            }
        }
    }
})
Olaf
źródło
Z jakiegoś powodu to nie zadziałało dla mnie. Tylko jawne wezwanie klasy zadziałało. Po prostu dodając dla innych. Myślę, że ma to coś wspólnego z tornadofx
nmu
Po utworzeniu pliku wejściowego testowej /src/test/resources, this.javaClass.getResource("/<test input filename>")działało zgodnie z oczekiwaniami. Dzięki za powyższe rozwiązanie.
jkwuc89,
co zrobić, jeśli fileContent nie ma wartości String i nie utworzę żadnego obiektu fikcyjnego?
minizibi
1
wiodący ukośnik przed ścieżką wydaje się tutaj obowiązkowy, podczas gdy w Javie zwykle go pomijam.
cakraww
6

Kotlin + Spring Way:

@Autowired
private lateinit var resourceLoader: ResourceLoader

fun load() {
    val html = resourceLoader.getResource("classpath:html/file.html").file
        .readText(charset = Charsets.UTF_8)
}
naXa
źródło
5
val fileContent = javaClass.getResource("/html/file.html").readText()
jivimberg
źródło
5
private fun loadResource(file: String) = {}::class.java.getResource(file).readText()
Olaf
źródło
4

Korzystanie z klasy zasobów biblioteki Google Guava :

import com.google.common.io.Resources;

val fileContent: String = Resources.getResource("/html/file.html").readText()
Lu55
źródło
Fajnie, że Guave zgłasza nazwę pliku, jeśli zasób nie zostanie znaleziony - znacznie lepsze do rozwiązywania problemów
Nishi
2

Oto sposób, w jaki wolę to robić:

fun getResourceText(path: String): String {
    return File(ClassLoader.getSystemResource(path).file).readText()
}
saidaspen
źródło
0

Może się przydać klasa File:

import java.io.File

fun main(args: Array<String>) {
  val content = File("src/main/resources/input.txt").readText()
  print(content)
} 
Krzysztof Ziomek
źródło
3
Ta odpowiedź jest myląca. To nie ładuje „zasobu”, ale ładuje plik bezpośrednio z systemu plików zamiast ze ścieżki klas. Nie będzie już działać po złożeniu aplikacji, ponieważ spróbujesz odwołać się do nieistniejących plików, zamiast ładować je z pliku jar.
Ben
@ben Dzięki za komentarz. Pytanie dotyczyło odczytu pliku z zasobu w teście kotlin Spek.
Krzysztof Ziomek