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") {
                ...
            }
        }
    }
})

Odpowiedzi:


116
val fileContent = MySpec::class.java.getResource("/html/file.html").readText()

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)
}

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")

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") {
                ...
            }
        }
    }
})

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)
}


5
private fun loadResource(file: String) = {}::class.java.getResource(file).readText()

4

Korzystanie z klasy zasobów biblioteki Google Guava :

import com.google.common.io.Resources;

val fileContent: String = Resources.getResource("/html/file.html").readText()

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()
}

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)
} 

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.