¿Cómo leer un archivo de texto de recursos en Kotlin?

94

Quiero escribir una prueba de Spek en Kotlin. La prueba debería leer un archivo HTML de la src/test/resourcescarpeta. ¿Cómo hacerlo?

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
fuente

Respuestas:

111
val fileContent = MySpec::class.java.getResource("/html/file.html").readText()
JB Nizet
fuente
30
Para mí, esto no funcionó, tuve que cambiarlo athis::class.java.classLoader.getResource("/html/file.html").readText()
pavlos163
4
Para mí, ambas opciones funcionaron en una aplicación de Android (observe el extra /en una de ellas, que debe eliminarse en la otra): this::class.java.getResource("/html/file.html").readText()ythis::class.java.classLoader.getResource("html/file.html").‌​readText()
Franco
19
val fileContent = javaClass.getResource("/html/file.html").readText()hace el trabajo aún más corto
Frank Neblung
28

otra solución ligeramente diferente:

@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
fuente
¡Buen uso de una función de extensión! Pero, ¿por qué usa una función lambda aquí? Eso no tiene mucho sentido para mí. Además, la thispieza no me funcionó. Por eso recomiendo lo siguiente:fun String.asResource(): URL? = object {}.javaClass.getResource(this)
Qw3ry
Me gusta este método de trabajo en el que declaras el archivo y luego trabajas en el contenido de ese archivo. Preferencia personal, supongo. thisen el ejemplo anterior se refiere al objeto de cadena.
jhodges
eso es un abuso terrible de la función de extensión. La carga de archivos no es una preocupación de la clase String.
Ben
es en este contexto. No lo haría disponible a nivel mundial ni lo usaría fuera de las clases de prueba. Lo considero más una función de mapeo aquí.
jhodges
26

No tengo idea de por qué esto es tan difícil, pero la forma más sencilla que he encontrado (sin tener que referirme a una clase en particular) es:

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

Y luego pasar una URL absoluta, p. Ej.

val html = getResourceAsText("/www/index.html")
Russell Briggs
fuente
se {}requiere? ¿Por qué no simplemente javaClass.getResource(path).readText()?
andrewgazelka
1
Se debe llamar a javaClass en un objeto de acuerdo con los documentos kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/java-class.html Si funciona sin él, entonces vaya de por vida :)
Russell Briggs
3
Una desventaja del método anterior es que crea un nuevo objeto para cada acceso a los recursos. Sería mejor almacenar el objeto ficticio fuera de la función.
Russell Briggs
@RussellBriggs tbh No creo que eso importe mucho. El rendimiento de la creación de un objeto no es realmente un problema si tiene acceso al disco.
Qw3ry
13

Una solución ligeramente diferente:

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
fuente
Por alguna razón, esto no funcionó para mí. Solo funcionaba llamar explícitamente a la clase. Solo agregando para otros. Creo que tiene algo que ver con tornadofx
nmu
Después de crear un archivo de entrada de prueba /src/test/resources, this.javaClass.getResource("/<test input filename>")funcionó como se esperaba. Gracias por la solución anterior.
jkwuc89
¿Qué hacer si fileContent no es String y no crearé ningún objeto ficticio?
minizibi
1
La barra inclinada antes de la ruta parece obligatoria aquí, mientras que en Java generalmente la omito.
cakraww
5
val fileContent = javaClass.getResource("/html/file.html").readText()
Jivimberg
fuente
5

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
fuente
5
private fun loadResource(file: String) = {}::class.java.getResource(file).readText()
Olaf
fuente
4

Usando la clase de recursos de la biblioteca de Google Guava :

import com.google.common.io.Resources;

val fileContent: String = Resources.getResource("/html/file.html").readText()
Lu55
fuente
es bueno que Guave informe un nombre de archivo si no se encuentra el recurso - mucho mejor para la resolución de problemas
Nishi
0

Puede encontrar útil la clase File:

import java.io.File

fun main(args: Array<String>) {
  val content = File("src/main/resources/input.txt").readText()
  print(content)
} 
Krzysztof Ziomek
fuente
3
Esta respuesta es engañosa. Eso no carga un "recurso" pero carga el archivo directamente desde el sistema de archivos, en lugar de la ruta de clases. Ya no funcionará después de ensamblar la aplicación, ya que tratará de hacer referencia a archivos no existentes, en lugar de cargarlos desde el archivo jar.
Ben
@ben Gracias por tu comentario. La pregunta fue sobre la lectura de archivos de recursos en la prueba de kotlin Spek.
Krzysztof Ziomek
0

Esta es la forma en que prefiero hacerlo:

fun getResourceText(path: String): String {
    return File(ClassLoader.getSystemResource(path).file).readText()
}
saidaspen
fuente