Skip to main content

Matchers compuestos

⏱ Dedicación recomendada: 0 minutos
Esto considera el contenido visible y relevante, e ignora texto colapsado o marcado como opcional.


r8vnhill/assertions-kt

Be lazy...

Puedes ejecutar el siguiente comando para crear el módulo

./gradlew setupComposedMatchersModule

Mientras se crean los archivos necesarios, puedes leer el código para saber qué está pasando.

convention-plugins/src/main/kotlin/composed.gradle.kts
val moduleName = "composed-matchers"
val moduleDir = rootProject.file(moduleName)
val printError: (String) -> Unit = System.err::println

tasks.register("setupComposedMatchersModule") {
group = "setup"
description =
"Creates the necessary files for the lesson on composed matchers"

doFirst {
val buildName = "build.gradle.kts"
createModuleDirectory()
createBuildFile(buildName)
createFiles(
packageName = "password",
"BeStrongPasswordTest.kt", "BeStrongPassword.kt"
)
createFiles(
packageName = "email",
"BeValidEmail.kt"
)
}
}

// Función para crear el directorio del módulo
// Si el directorio ya existe, imprime un mensaje de error; de lo contrario,
// intenta crearlo
fun createModuleDirectory() = moduleDir.run {
when {
exists() -> printError("Directory already exists: $absolutePath")
mkdirs() -> println("Directory created: $absolutePath")
else -> printError("Failed to create directory: $absolutePath")
}
}

// Función para crear el archivo 'build.gradle.kts'
// Si el archivo ya existe, imprime un mensaje de error; de lo contrario, lo
// crea con un comentario predeterminado
fun createBuildFile(buildName: String) =
moduleDir.resolve(buildName).run {
if (!exists()) {
writeText("// Intentionally left blank")
println("File created: $absolutePath")
} else {
printError("File already exists: $absolutePath")
}
}

// Función para crear archivos en un paquete específico
// Si un archivo ya existe, imprime un mensaje de error; de lo contrario, lo
// crea con un paquete predeterminado
fun createFiles(
packageName: String, vararg files: String
) {
val group = rootProject.group.toString()
files.forEach { fileName ->
moduleDir.resolve(
"src/test/kotlin/${group.replace(".", "/")}/$packageName"
).run {
mkdirs() // Crea los directorios necesarios
resolve(fileName).run {
if (exists()) {
printError("File already exists: $this")
} else {
writeText("package $group.$packageName\n\n")
println("File created: $this")
}
}
}
}
}

Preocúpate de que el plugin composed-matchers esté aplicado en el archivo build.gradle.kts de tu proyecto.

./gradlew setupComposedMatchersModule

Preocúpate de que el nuevo módulo esté incluido en el archivo settings.gradle.kts.

Los matchers se pueden combinar para crear reglas más complejas mediante la composición de otros matchers más simples. Esto permite verificar condiciones complejas de forma declarativa y legible.

Existen dos operaciones lógicas clave para componer matchers:

  • Suma lógica (Matcher.any o or): Se satisface si al menos uno de los matchers es verdadero.
  • Producto lógico (Matcher.all o and): Se satisface solo si todos los matchers son verdaderos.

Ejemplo de composición de matchers

Vamos a implementar un matcher para verificar que una contraseña es "fuerte". En este contexto, una contraseña fuerte debe:

  • Contener al menos un número.
  • Contener al menos una letra mayúscula.
  • Contener al menos una letra minúscula.
  • Tener al menos 16 caracteres.
val beStrongPassword = containADigit() and
contain("[A-Z]".toRegex()) and
contain("[a-z]".toRegex()) and
haveMinLength(16)

Uso en las pruebas

Una vez que hemos definido el matcher beStrongPassword, podemos utilizarlo en nuestras pruebas para asegurarnos de que las contraseñas cumplen con los requisitos de fortaleza:

"Password1AaBbCcDdEeFf" should beStrongPassword
"PasswordAaBbCcDdEeFf" shouldNot beStrongPassword

La composición de matchers es una herramienta poderosa que permite crear validaciones complejas de manera concisa y reutilizable.

Ejercicio: Validación de Correo Electrónico

Crea un matcher compuesto que valide si una cadena es un correo electrónico válido. Un correo válido debe:

  1. Contener un símbolo @.
  2. Tener al menos un carácter antes del @.
  3. Tener al menos un carácter después del @ y antes del ..
  4. Terminar con un dominio que tenga al menos 2 caracteres después del ..
Ver hints
  • Puedes utilizar indexOf: String.(String) -> Int para encontrar la posición de un carácter en una cadena.
  • Puedes utilizar substringAfterLast: String.(String) -> String para obtener la parte de una cadena después de la última ocurrencia de un carácter.
  • Algunos matchers útiles son contain y haveLengthAtLeast.
Solución
composed-matchers/src/test/kotlin/com/github/username/email/BeValidEmail.kt
fun beValidEmail(): Matcher<String> {
val containsAt = contain("@")
val hasTextBeforeAt = Matcher<String> { value ->
MatcherResult(
value.indexOf("@") > 0,
{ "The email should have text before '@'" },
{ "The email has valid text before '@'" }
)
}
val hasTextAfterAt = Matcher<String> { value ->
MatcherResult(
value.indexOf("@") < value.lastIndexOf(".") - 1,
{ "The email should have text between '@' and '.'" },
{ "The email has valid text between '@' and '.'" }
)
}
val validDomain = Matcher<String> { value ->
MatcherResult(
value.substringAfterLast(".").length >= 2,
{ "The domain should have at least 2 characters" },
{ "The domain is valid" }
)
}

return containsAt and hasTextBeforeAt and hasTextAfterAt and validDomain
}

¿Qué aprendimos?

En esta sección exploramos cómo utilizar matchers compuestos para validar condiciones complejas de forma modular y reutilizable.

Puntos clave

  • Los matchers se pueden combinar usando operaciones lógicas como suma lógica (or / Matcher.any) y producto lógico (and / Matcher.all) para crear validaciones más sofisticadas.
  • Al combinar matchers simples, logramos verificar condiciones complejas, como la fortaleza de una contraseña o la validez de un correo electrónico.
  • La composición de matchers es útil para mantener nuestras pruebas limpias y legibles, a la vez que facilita la reutilización del código.

Al final, hemos visto cómo estos conceptos se aplican a escenarios prácticos y cómo los matchers permiten crear reglas concisas para validar datos en nuestras pruebas.

Bibliografías Recomendadas