Saltar al contenido principal

Prueba tu aplicación y código

Aprende cómo escribir pruebas para tu código Dart usando la librería `package:test`.

En este capítulo, aprenderás a escribir pruebas para tu código Dart. Las pruebas son fundamentales para asegurar que tu aplicación se comporte como se espera y permanezca estable a medida que realizas cambios. Usarás la librería package:test, un framework de pruebas popular para Dart, para escribir pruebas unitarias para los modelos de datos que creaste en el capítulo anterior.

Qué lograrás

Agregar el paquete test como una dev dependency (dependencia de desarrollo)
Escribir pruebas estructuradas con el paquete test y matchers
Probar la lógica de deserialización JSON

Prerrequisitos

#

Antes de comenzar este capítulo, asegúrate de:

  • Haber completado el Capítulo 9 y tener un entorno de desarrollo de Dart en funcionamiento con el proyecto dartpedia.
  • Estar familiarizado con conceptos básicos de programación como variables, funciones y flujo de control.
  • Entender el propósito de las pruebas en el desarrollo de software.

Tareas

#

En este capítulo, agregarás pruebas al paquete wikipedia, asegurando que la lógica de deserialización JSON para tus modelos de datos funcione correctamente.

Tarea 1: Agregar la dependencia test

#

Primero, debes confirmar que el paquete test ya es una dependencia de desarrollo en tu proyecto.

  1. Abre el archivo wikipedia/pubspec.yaml dentro de tu proyecto.

  2. Localiza la sección dev_dependencies.

  3. Verifica que test: ^1.24.0 (o la última versión estable) esté presente bajo dev_dependencies.

    yaml
    dev_dependencies:
      lints: ^5.0.0
      test: ^1.24.0
    

    Si falta la dependencia test, agrégala a tu archivo pubspec.yaml. El símbolo ^ permite usar versiones compatibles.

  4. Si realizaste algún cambio en el archivo, guarda pubspec.yaml y ejecuta dart pub get en tu terminal desde el directorio wikipedia. Este comando obtiene cualquier dependencia recién agregada y la hace disponible para su uso en tu proyecto.

    Deberías ver una salida similar a esta:

    bash
    Resolving dependencies...
    Downloading packages...
    + test 1.25.1
    Changed 2 dependencies!
    

Tarea 2: Crear un archivo de prueba y agregar imports

#

A continuación, crea un archivo de prueba para tus modelos de datos y agrégale las importaciones necesarias.

  1. Navega al directorio wikipedia/test.

  2. Crea un nuevo archivo llamado model_test.dart en este directorio.

  3. Abre el archivo wikipedia/test/model_test.dart y agrega las siguientes sentencias import en la parte superior del archivo:

    dart
    import 'dart:convert';
    import 'dart:io';
    
    import 'package:test/test.dart';
    import 'package:wikipedia/src/model/article.dart';
    import 'package:wikipedia/src/model/search_results.dart';
    import 'package:wikipedia/src/model/summary.dart';
    
    const String dartLangSummaryJson = './test/test_data/dart_lang_summary.json';
    const String catExtractJson = './test/test_data/cat_extract.json';
    const String openSearchResponse = './test/test_data/open_search_response.json';
    

    Estas líneas importan el paquete test, que proporciona el framework de pruebas, y los archivos del modelo de datos que deseas probar. Las cadenas constantes declaran la ubicación de tus datos de muestra.

Tarea 3: Crear los archivos de datos de prueba

#

Las pruebas que necesitas escribir dependen de archivos JSON locales que imitan las respuestas de la API de Wikipedia. Necesitas crear un directorio test_data y poblarlo con tres archivos.

  1. Navega al directorio wikipedia/test.

  2. Crea un nuevo directorio llamado test_data.

  3. Dentro del directorio test_data, crea un nuevo archivo llamado dart_lang_summary.json y pega el siguiente contenido en él:

    json
    {
      "type": "standard",
      "title": "Dart (programming language)",
      "displaytitle": "<span class=\"mw-page-title-main\">Dart (programming language)</span>",
      "namespace": {
          "id": 0,
          "text": ""
      },
      "wikibase_item": "Q406009",
      "titles": {
        "canonical": "Dart_(programming_language)",
        "normalized": "Dart (programming language)",
        "display": "<span class=\"mw-page-title-main\">Dart (programming language)</span>"
      },
      "pageid": 33033735,
      "lang": "en",
      "dir": "ltr",
      "revision": "1259309990",
      "tid": "671bc7c6-aa67-11ef-aa2a-7c1da4fbe8fb",
      "timestamp": "2024-11-24T13:24:16Z",
      "description": "Programming language",
      "description_source": "local",
      "content_urls": {
        "desktop": {
          "page": "https://en.wikipedia.org/wiki/Dart_(programming_language)",
          "revisions": "https://en.wikipedia.org/wiki/Dart_(programming_language)?action=history",
          "edit": "https://en.wikipedia.org/wiki/Dart_(programming_language)?action=edit",
          "talk": "https://en.wikipedia.org/wiki/Talk:Dart_(programming_language)"
        },
        "mobile": {
          "page": "https://en.m.wikipedia.org/wiki/Dart_(programming_language)",
          "revisions": "https://en.m.wikipedia.org/wiki/Special:History/Dart_(programming_language)",
          "edit": "https://en.m.wikipedia.org/wiki/Dart_(programming_language)?action=edit",
          "talk": "https://en.m.wikipedia.org/wiki/Talk:Dart_(programming_language)"
        }
      },
      "extract": "Dart is a programming language designed by Lars Bak and Kasper Lund and developed by Google. It can be used to develop web and mobile apps as well as server and desktop applications.",
      "extract_html": "<p><b>Dart</b> is a programming language designed by Lars Bak and Kasper Lund and developed by Google. It can be used to develop web and mobile apps as well as server and desktop applications.</p>"
    }
    
  4. A continuación, crea un archivo llamado cat_extract.json. Este archivo es muy largo, así que copia el contenido desde este enlace: https://github.com/ericwindmill/dash_getting_started/blob/main/dart_step_by_step/step_10/wikipedia/test/test_data/cat_extract.json

  5. A continuación, crea un archivo llamado open_search_response.json y pega este contenido en él:

    json
    [
        "dart",
        [
            "Dart",
            "Darth Vader",
            "Dartmouth College",
            "Darts",
            "Darth Maul",
            "Dartford Crossing",
            "Dart (programming language)",
            "Dartmouth College fraternities and sororities",
            "Dartmoor",
            "Dartmouth, Massachusetts"
        ],
        [
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            "",
            ""
        ],
        [
            "https://en.wikipedia.org/wiki/Dart",
            "https://en.wikipedia.org/wiki/Darth_Vader",
            "https://en.wikipedia.org/wiki/Dartmouth_College",
            "https://en.wikipedia.org/wiki/Darts",
            "https://en.wikipedia.org/wiki/Darth_Maul",
            "https://en.wikipedia.org/wiki/Dartford_Crossing",
            "https://en.wikipedia.org/wiki/Dart_(programming_language)",
            "https://en.wikipedia.org/wiki/Dartmouth_College_fraternities_and_sororities",
            "https://en.wikipedia.org/wiki/Dartmoor",
            "https://en.wikipedia.org/wiki/Dartmouth,_Massachusetts"
        ]
    ]
    

Con estos archivos en su lugar, estás listo para escribir las pruebas que verificarán tus modelos de datos.

Tarea 4: Escribir pruebas para la deserialización JSON

#

Ahora, escribirás pruebas para la lógica de deserialización JSON en tus modelos de datos. Usarás las funciones group, test y expect del paquete test.

  1. Usa la función group para agrupar pruebas relacionadas. Agrega lo siguiente a tu archivo wikipedia/test/model_test.dart:

    dart
    void main() {
      group('deserialize example JSON responses from wikipedia API', () {
        // Tests will go here
      });
    }
    

    La función group toma una descripción del grupo y una función callback que contiene las pruebas.

  2. Crea una prueba para el modelo Summary. Agrega la siguiente función test dentro de la función group:

    dart
    void main() {
      group('deserialize example JSON responses from wikipedia API', () {
        test('deserialize Dart Programming Language page summary example data from '
            'json file into a Summary object', () async {
          final String pageSummaryInput =
              await File(dartLangSummaryJson).readAsString();
          final Map<String, Object?> pageSummaryMap =
              jsonDecode(pageSummaryInput) as Map<String, Object?>;
          final Summary summary = Summary.fromJson(pageSummaryMap);
          expect(summary.titles.canonical, 'Dart_(programming_language)');
        });
      });
    }
    

    Esta función test hace lo siguiente:

    • Lee el contenido del archivo dart_lang_summary.json.
    • Decodifica la cadena JSON en un Map<String, Object?>.
    • Crea un objeto Summary a partir del mapa usando el constructor Summary.fromJson.
    • Usa la función expect para afirmar que la propiedad canonical del objeto titles es igual a 'Dart_(programming_language)'.

    La función expect toma un valor y un matcher. El matcher se usa para afirmar que el valor cumple con ciertos criterios. En este caso, se usa el matcher equals para afirmar que el valor es igual a una cadena específica.

  3. Crea una prueba para el modelo Article. Agrega la siguiente función test dentro de la función group, después de la prueba anterior:

    dart
    void main() {
      group('deserialize example JSON responses from wikipedia API', () {
        test('deserialize Dart Programming Language page summary example data from '
            'json file into a Summary object', () async {
          final String pageSummaryInput =
              await File(dartLangSummaryJson).readAsString();
          final Map<String, Object?> pageSummaryMap =
              jsonDecode(pageSummaryInput) as Map<String, Object?>;
          final Summary summary = Summary.fromJson(pageSummaryMap);
          expect(summary.titles.canonical, 'Dart_(programming_language)');
        });
    
        test('deserialize Cat article example data from json file into '
            'an Article object', () async {
          final String articleJson = await File(catExtractJson).readAsString();
          final Map<String, Object?> articleMap =
              jsonDecode(articleJson) as Map<String, Object?>;
          final List<Article> articles = Article.listFromJson(articleMap);
          expect(articles.first.title.toLowerCase(), 'cat');
        });
      });
    }
    

    Esta función test hace lo siguiente:

    • Lee el contenido del archivo cat_extract.json.
    • Decodifica la cadena JSON en un Map<String, Object?>.
    • Crea el objeto List<Article> a partir del mapa usando el método estático Article.listFromJson.
    • Usa la función expect para afirmar que la propiedad title del primer artículo es igual a 'cat'.
  4. Crea una prueba para el modelo SearchResults. Agrega la siguiente función test dentro de la función group, después de la prueba anterior:

    dart
    void main() {
      group('deserialize example JSON responses from wikipedia API', () {
        test('deserialize Dart Programming Language page summary example data from '
              'json file into a Summary object', () async {
          final String pageSummaryInput =
              await File(dartLangSummaryJson).readAsString();
          final Map<String, Object?> pageSummaryMap =
              jsonDecode(pageSummaryInput) as Map<String, Object?>;
          final Summary summary = Summary.fromJson(pageSummaryMap);
          expect(summary.titles.canonical, 'Dart_(programming_language)');
        });
    
        test('deserialize Cat article example data from json file into '
            'an Article object', () async {
          final String articleJson = await File(catExtractJson).readAsString();
          final Map<String, Object?> articleMap =
              jsonDecode(articleJson) as Map<String, Object?>;
          final List<Article> articles = Article.listFromJson(articleMap);
          expect(articles.first.title.toLowerCase(), 'cat');
        });
    
        test('deserialize Open Search results example data from json file '
            'into an SearchResults object', () async {
          final String resultsString =
              await File(openSearchResponse).readAsString();
          final List<Object?> resultsAsList =
              jsonDecode(resultsString) as List<Object?>;
          final SearchResults results = SearchResults.fromJson(resultsAsList);
          expect(results.results.length, greaterThan(1));
        });
      });
    }
    

    Esta función test hace lo siguiente:

    • Lee el contenido del archivo open_search_response.json.
    • Decodifica la cadena JSON en un List<Object?>.
    • Crea un objeto SearchResults a partir de la lista usando el constructor SearchResults.fromJson.
    • Usa la función expect para afirmar que la lista results tiene una longitud mayor que 1.

Tarea 5: Ejecutar las pruebas

#

Ahora que has escrito las pruebas, puedes ejecutarlas para verificar que pasen.

  1. Abre tu terminal y navega al directorio wikipedia.

  2. Ejecuta el comando dart test.

    Deberías ver una salida similar a esta:

    bash
    00:02 +4: All tests passed!
    

    Esto confirma que las tres pruebas están pasando.

Revisión

#

Qué lograste

Aquí tienes un resumen de lo que construiste y aprendiste en esta lección.
Agregaste el paquete test como una dev dependency

Agregaste test como una dev dependency en pubspec.yaml y creaste archivos de prueba en el directorio test/. Las dev dependencies de pub están destinadas a ser utilizadas durante el desarrollo y las pruebas, no en producción. Como resultado, no se pueden usar en el directorio lib.

Escribiste pruebas estructuradas con el paquete test y matchers

Usaste group() para organizar pruebas relacionadas, test() para definir casos de prueba individuales, y expect() con matchers como equals y greaterThan para afirmar el comportamiento esperado. Ejecutar dart test ejecuta todas las pruebas declaradas por tu paquete.

Probaste la lógica de deserialización JSON

Creaste archivos de datos de prueba con JSON de muestra y escribiste pruebas para verificar que tus métodos fromJson parsean correctamente los datos en objetos Dart. Esto asegura que tus modelos manejen adecuadamente las respuestas reales de la API.

Cuestionario

#

Pon a prueba tu comprensión

1 / 3
What's the relationship between group, test, and expect in Dart's testing library?
  1. group ejecuta pruebas, test define aserciones, expect organiza la salida.

    No exactamente.

    Los roles están mezclados aquí. Piensa en lo que sugiere cada nombre: "group" (grupo) implica organización, "test" (prueba) implica un caso de prueba.

  2. group organiza pruebas relacionadas, test define casos de prueba individuales, expect afirma que los valores coinciden con las expectativas.

    ¡Correcto!

    Los grupos contienen pruebas, las pruebas contienen expectativas. Esta jerarquía mantiene tu conjunto de pruebas (test suite) organizado y tus aserciones claras.

  3. expect debe llamarse antes de test, el cual debe llamarse antes de group.

    No exactamente.

    El orden de anidación está invertido en esta respuesta. Piensa en qué elemento es el contenedor y cuál es el contenido.

  4. group y test son intercambiables, y expect los ejecuta a ambos.

    No exactamente.

    No son intercambiables. Cada uno sirve para un propósito distinto al estructurar tu código de prueba.

In expect(results.length, greaterThan(1)), what is greaterThan(1) called and what does it do?
  1. Un matcher (emparejador). Describe la condición que debe cumplir el valor real.

    ¡Correcto!

    Matchers como greaterThan, equals, contains e isNull describen las condiciones esperadas. Hacen que las aserciones de prueba sean legibles y proporcionan mensajes de fallo útiles.

  2. Un predicado. Filtra los resultados antes de la prueba.

    No exactamente.

    Los predicados se utilizan típicamente para filtrar colecciones. En expect, the segundo argumento sirve para un rol diferente al de filtrado.

  3. Un comparador. Devuelve true o false para ordenar.

    No exactamente.

    Los comparadores se usan para ordenar colecciones. Esto sirve para un propósito diferente en el contexto de las pruebas.

  4. Un validador. Lanza una excepción si el valor es incorrecto.

    No exactamente.

    Cerca, pero no del todo. Los matchers no lanzan excepciones directamente, sino que devuelven un resultado que expect utiliza para pasar o fallar la prueba.

You write a test that calls await File(path).readAsString(). What do you need to add to the test function for this to work?
  1. Marcar el callback de la prueba como async: test('...', () async { ... })

    ¡Correcto!

    Al igual que las funciones normales de Dart, los callbacks de prueba pueden ser async. El framework de pruebas espera automáticamente a que se complete el Future devuelto.

  2. Usar testAsync en lugar de test.

    No exactamente.

    No existe la función testAsync. La función test normal puede manejar operaciones asíncronas con la modificación correcta en su callback.

  3. Llama a await Future.delayed(Duration.zero) primero para habilitar el modo asíncrono.

    No exactamente.

    No se necesita ninguna inicialización o retraso especial. El cambio que necesitas es en la firma de la función, no en su cuerpo.

  4. Envolver la lectura del archivo en una llamada a Future.sync().

    No exactamente.

    No hay Future.sync() en Dart. Piensa en lo que haces normalmente cuando una función usa await.

Siguiente lección

#

En la siguiente lección, implementarás las llamadas a la API de Wikipedia en tu aplicación dartpedia. Usarás el paquete http para obtener datos de la API de Wikipedia y mostrárselos al usuario.