Skip to main content
La Trailblazer Community en Slack ya está en fase beta abierta. Únase y conéctese en tiempo real. Únase a nosotros en Slack

Información general sobre pruebas de unidad de Apex

Objetivos de aprendizaje

Después de completar esta unidad, podrá:

  • Describir las ventajas clave de las pruebas de unidad de Apex.
  • Definir una clase con métodos de prueba.
  • Ejecutar todos los métodos de prueba en una clase e inspeccionar los errores.
  • Crear y ejecutar un conjunto de clases de prueba.
Nota

Nota

¿Es su idioma de aprendizaje español (LATAM)? Comience el reto en un Trailhead Playground en español (LATAM) y utilice las traducciones entre paréntesis para navegar. Copie y pegue solo los valores en inglés, ya que las validaciones del reto dependen de los datos en ese idioma. Si no aprueba el reto en su organización en español (LATAM), recomendamos que (1) cambie la configuración local a Estados Unidos, (2) cambie el idioma a inglés (según estas instrucciones) y, luego, (3) haga clic en el botón “Check Challenge” (Comprobar el reto) nuevamente.

Consulte la insignia Trailhead en su idioma para obtener más información sobre cómo aprovechar la experiencia de Trailhead en otros idiomas.

Pruebas de unidad de Apex

El marco de trabajo de pruebas de Apex permite escribir y ejecutar pruebas para desencadenadores y clases de Apex. Las pruebas de unidad de Apex le permiten garantizar la alta calidad de su código Apex y cumplir los requisitos para la implementación de Apex.

Las pruebas son la clave para el desarrollo correcto a largo plazo y un componente decisivo del proceso de desarrollo. El marco de pruebas de Apex facilita la prueba del código Apex. El código Apex solo se puede escribir en un sandbox o una organización de desarrollador, pero no en una organización de producción. El código Apex se puede implementar en una organización de producción desde un sandbox. Además, los desarrolladores de aplicaciones pueden distribuir el código Apex a los clientes desde sus organizaciones de desarrollador mediante la carga de paquetes a Salesforce AgentExchange. Además de ser esenciales para garantizar la calidad, las pruebas de unidad de Apex son además requisitos para la implementación y distribución de Apex.

Las pruebas de unidad de Apex generan mejores códigos. El marco de trabajo de pruebas de Apex:

  • Garantiza que los desencadenadores y las clases de Apex funcionen según lo esperado.
  • Dispone de un conjunto de pruebas de regresión que se pueden volver a ejecutar cada vez que se actualicen las clases y los desencadenadores para garantizar que las actualizaciones futuras que realice en la aplicación no afecten a la funcionalidad existente.
  • Cumple los requisitos de cobertura de código para la implementación de Apex para producción o la distribución de Apex a los clientes mediante paquetes.
  • Ofrece aplicaciones de alta calidad a la organización de producción, lo que aumenta la productividad de los usuarios de producción.
  • Ofrece aplicaciones de alta calidad a suscriptores de paquetes, lo que aumenta la confianza de los clientes.

Requisito de cobertura de código para la implementación

A fin de poder implementar su código o empaquetarlo para Salesforce AgentExchange, como mínimo el 75 % del código Apex debe estar cubierto por pruebas y todas deben aprobar. Además, cada desencadenador debe tener determinada cobertura. Aunque la cobertura de código sea un requisito para la implementación, no escriba pruebas solamente para cumplir este requisito. Asegúrese de probar casos de uso en su aplicación, lo que incluye casos de pruebas positivas y negativas, y el procesamiento masivo y de un solo registro.

Clase de prueba y sintaxis de métodos de prueba

Las clases de prueba de Apex se anotan con @IsTest. Las clases de prueba se pueden definir con modificadores de acceso privado o público. Si va a usar una clase de prueba para pruebas de unidad únicamente, declárela como privada. Las clases de prueba públicas se suelen usar para las clases de fábrica de datos de prueba, que vamos a abarcar en la unidad Crear datos de prueba para pruebas de Apex. Las clases que se definen como @IsTest deben ser clases de nivel superior.

Dentro de la clase de prueba, los métodos de prueba también se definen con la anotación @IsTest. Dado que la visibilidad de un método no es importante, la declaración de un método de prueba como público o privado no supone ninguna diferencia, ya que el marco de pruebas siempre puede acceder a los métodos de prueba. Por este motivo, los modificadores de acceso se omiten en la sintaxis del método.

En este ejemplo de código podemos ver una definición de una clase de prueba con un método de prueba.

@IsTest
private with sharing class MyTestClass {
  @IsTest static void myTest() {
    // code_block
  }
}

La anotación @IsTest puede llevar varios modificadores entre paréntesis, que están separados por un espacio. Hablaremos brevemente sobre la anotación @IsTest(seeAllData=true) en la unidad Crear datos de prueba para pruebas de Apex.

Ejemplo de prueba de unidad: Probar la clase TemperatureConverter

El siguiente ejemplo sencillo es una clase de prueba con tres métodos de prueba. El método de clase que se prueba obtiene la temperatura en grados Fahrenheit como una entrada. Convierte este valor de temperatura en grados Celsius y devuelve el resultado convertido. Vamos a agregar la clase personalizada y su clase de prueba.

  1. Abra la Consola web desde el menú de acceso rápido (Ícono de engranaje de configuración).
  2. En la Consola web, presione Ctrl+Shift+P (Windows) o ⌘+Shift+P (macOS) para abrir la Paleta de comandos.
  3. Ingrese SFDX: Create Apex Class (SFDX: Crear una clase de Apex) y presione Intro.
  4. Para el tipo de plantilla, seleccione DefaultApexClass y, luego, presione Intro.
  5. Para el nombre de clase, ingrese TemperatureConverter y, luego, presione Intro.
  6. Para el directorio de salida, seleccione force-app/main/default/classes y, luego, presione Intro.
  7. Sustituya el cuerpo de clase predeterminado por el siguiente.
public with sharing class TemperatureConverter {
  // Takes a Fahrenheit temperature and returns the Celsius equivalent.
  public static Decimal FahrenheitToCelsius(Decimal fh) {
    Decimal cs = (fh - 32) * 5/9;
    return cs.setScale(2);
  }
}
  1. Para guardar e implementar su clase, presione Ctrl+S (Windows) o ⌘+S (macOS).
  2. Repita los pasos anteriores para crear la clase TemperatureConverterTest. Sustituya el cuerpo de clase predeterminado por el siguiente.
    @IsTest
    private with sharing class TemperatureConverterTest {
      @IsTest static void testWarmTemp() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(70);
        Assert.areEqual(21.11,celsius);
      }
      @IsTest static void testFreezingPoint() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(32);
        Assert.areEqual(0,celsius);
      }
      @IsTest static void testBoilingPoint() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(212);
        Assert.areEqual(100,celsius,'Boiling point temperature is not expected.');
      }
      @IsTest static void testNegativeTemp() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(-10);
        Assert.areEqual(-23.33,celsius);
      }
    }

La clase de prueba TemperatureConverterTest verifica si el método funciona según lo esperado mediante la llamada a dicho método con distintas entradas para la temperatura en grados Fahrenheit. Cada método de prueba verifica un solo tipo de entrada: temperatura alta, punto de congelación, punto de ebullición y valor de temperatura negativo. Las verificaciones se realizan llamando al método Assert.areEqual() que solo lleva dos parámetros. El primer parámetro es el valor esperado y el segundo es el valor real. Este método también acepta un tercer parámetro, que es una cadena que describe la comparación. Este tercer parámetro opcional se usa en el ejemplo testBoilingPoint(). La cadena especificada se registra si se produce un error de afirmación.

Habilitar resultados de cobertura de código

Antes de ejecutar las pruebas, habilite la Consola web para recuperar los resultados de cobertura de código.

  1. En la Consola web, haga clic en Ícono de menú en la parte superior de la barra de actividades.
  2. Seleccione File | Preferences | Settings (Archivo | Preferencias | Configuración) para abrir la ventana Settings (Configuración).
  3. En el cuadro de búsqueda, ingrese retrieve (recuperar).
  4. Habilite la configuración Salesforcedx-vscode-apex-testing: Retrieve-test-code-coverage.
  5. Cierre la ventana.

Ahora ejecutemos los métodos de esta clase de prueba.

  1. En la Consola web, haga clic en Ícono de pruebas en la barra de actividades. Se abre el panel lateral Testing (Pruebas).
  2. En la barra de búsqueda de Testing (Pruebas), ingrese TemperatureConverterTest.
  3. Para ejecutar todos los métodos de prueba en la clase, pase el cursor sobre la línea TemperatureConverterTest y haga clic en Ícono de Run Test (Realizar prueba).

En el panel Test Results (Resultados de prueba), puede ver los resultados de sus pruebas. En el panel lateral Testing (Pruebas), los métodos de prueba ahora tienen marcas de verificación verdes, lo que indica que todas las pruebas se aprobaron. La Consola web también genera archivos JSON y Markdown con los resultados de las pruebas, que se encuentran en el directorio .sfdx/tools/testresults/apex.

Los resultados de las pruebas de la clase TemperatureConverterTest. El ícono del panel lateral Testing (Pruebas), los métodos de prueba aprobados en el panel lateral Testing (Pruebas), el archivo Markdown con los resultados de las pruebas y el panel Test Results (Resultados de prueba) están resaltados.

Una vez ejecutadas las pruebas, la cobertura de código se genera automáticamente para las clases y los desencadenadores de Apex de la organización. Puede verificar el porcentaje de cobertura de código en el archivo Markdown con los resultados de las pruebas. En este ejemplo, la clase TemperatureConverter tiene una cobertura del 100 %, como se muestra en la imagen.

El porcentaje de cobertura de código por clase en la Consola web. El archivo Markdown con los resultados de las pruebas está resaltado.

Note

Siempre que modifique el código Apex, vuelva a ejecutar las pruebas para actualizar los resultados de la cobertura de código.

Aunque un solo método de prueba puede tener como resultado la cobertura completa de la clase TemperatureConverter, es importante probar con distintas entradas para garantizar la calidad del código. Obviamente, no es posible verificar cada punto de datos, pero puede probar puntos de datos comunes y distintos intervalos de entrada. Por ejemplo, puede verificar el paso de números positivos y negativos, valores de límite y valores de parámetros no válidos para confirmar un comportamiento negativo. Las pruebas para la clase TemperatureConverter permiten verificar puntos de datos comunes, como el punto de ebullición y congelación, así como la temperatura positiva y negativa.

Sin embargo, la clase de prueba TemperatureConverterTest actualmente no abarca las condiciones de límite ni las entradas no válidas. Las condiciones de límite son los valores mínimos y máximos que el método puede manejar. Para las entradas no válidas, considere qué sucede si null (nulo) pasa como argumento para FahrenheitToCelsius(). En este caso, cuando el tiempo de ejecución de Apex elimina la referencia a la variable de parámetro para evaluar la fórmula, genera System.NullPointerException. Para solucionar este error, puede modificar el método FahrenheitToCelsius() para verificar entradas no válidas y devolver null (nulo) en ese caso. Luego, agregue un método de prueba a la clase TemperatureConverterTest para verificar el comportamiento de la entrada no válida.

En este punto, todas las pruebas se aprueban dado que la fórmula de conversión usada en el método de clase es correcta. Sin embargo, este es un proceso aburrido. Vamos a simular un error solamente para ver qué sucede cuando una afirmación genera un error. Por ejemplo, vamos a modificar la prueba de punto de ebullición y a pasar un valor esperado falso para el punto de ebullición en grados Celsius (0 en lugar de 100). Esto genera un error del método de prueba correspondiente.

  1. Cambie el método de prueba testBoilingPoint() al siguiente código y, a continuación, guarde la clase de prueba actualizada.
@IsTest
static void testBoilingPoint() {
  Decimal celsius = TemperatureConverter.FahrenheitToCelsius(212);
  // Simulate failure
  Assert.areEqual(0,celsius,'Boiling point temperature is not expected.');
}
  1. Desde el panel lateral Testing (Pruebas), ejecute todos los métodos de prueba en la clase TemperatureConverterTest. La afirmación de testBoilingPoint() no surte efecto y genera un error grave (excepción de tipo AssertException que no se puede capturar).
  2. Verifique los resultados en el panel Test Results (Resultados de pruebas). La ejecución de pruebas indica una de cuatro pruebas con errores.
  3. Para obtener el mensaje de error de la falla en la prueba, seleccione testBoilingPoint desde el panel lateral Testing (Pruebas). El mensaje de error incluye el texto que se proporcionó en la declaración Assert.areEqual(): System.AssertException: Assertion Failed: Boiling point temperature is not expected.: Expected: 0, Actual: 100.00

Los nuevos resultados de pruebas para la clase TemperatureConverterTest, donde la afirmación en el método testBoilingPoint() falla.

Los datos de estos métodos de prueba son números y no registros de Salesforce. Encontrará más información sobre cómo probar los registros de Salesforce y configurar los datos en la siguiente unidad.

Aumentar la cobertura de código

Al escribir pruebas, intente alcanzar la máxima cobertura de código posible. Evite alcanzar una cobertura de tan solo el 75 %, ya que esta es la cobertura mínima que requiere Salesforce para las implementaciones y los paquetes. Cuantos más casos de prueba cubren las pruebas, mayor es la probabilidad de que el código sea consistente. En ocasiones, incluso después de escribir métodos de prueba para todos los métodos de clase, la cobertura de código no es del 100%. Una causa común es que no se cubren todos los valores de datos para la ejecución de código condicional. Por ejemplo, algunos valores de datos tienden a ser ignorados si el método de clase tiene declaraciones if que dan lugar a distintas ramas para su ejecución en función de si se cumple la condición evaluada. Asegúrese de que los métodos de prueba representan estos valores distintos.

En este ejemplo se incluye el método de clase getTaskPriority(), el cual contiene dos declaraciones if. La tarea principal de este método es devolver un valor de cadena de prioridad basado en el estado de un prospecto determinado. El método valida el estado primero y devuelve null si el estado no es válido. Si el estado es CA, el método devuelve 'High'. En caso contrario, devuelve 'Normal' para cualquier otro valor de estado.

public with sharing class TaskUtil {
  public static String getTaskPriority(String leadState) {
    // Validate input
    if(String.isBlank(leadState) || leadState.length() > 2) {
      return null;
    }
    String taskPriority;
    if(leadState == 'CA') {
      taskPriority = 'High';
    } else {
      taskPriority = 'Normal';
    }
    return taskPriority;
  }
}
Note

El operador de igualdad (==) realiza comparaciones de cadenas sin distinción entre mayúsculas y minúsculas para eliminar la necesidad de convertir la cadena a minúsculas primero. Esto significa que pasar 'ca' o 'Ca' cumplirá la condición de igualdad con el literal de cadena 'CA'.

Esta es la clase de prueba para el método getTaskPriority(). El método de prueba simplemente llama a getTaskPriority() con un solo estado (NY).

@IsTest
private with sharing class TaskUtilTest {
  @IsTest
  static void testTaskPriority() {
    String pri = TaskUtil.getTaskPriority('NY');
    Assert.areEqual('Normal', pri);
  }
}

Vamos a ejecutar este clase de prueba (TaskUtilTest) en la Consola web y a comprobar la cobertura de código para la clase TaskUtil correspondiente que cubre esta prueba. Una vez ejecutada la prueba, la cobertura de código para TaskUtil se indica como un 75%.

Ahora abra el archivo TaskUtil.cls. Con este archivo abierto, haga clic en ícono de tres barras en la parte inferior de la ventana de la Consola web para habilitar la configuración Highlight Apex Code Coverage (Resaltar la cobertura de código Apex). Con esta configuración habilitada, verá seis líneas en verde (cubiertas) y dos líneas en rojo (no cubiertas) en la clase TaskUtil, como se muestra en esta imagen.

Líneas cubiertas para la clase TaskUtil en la Consola web. La configuración Highlight Apex Code Coverage (Resaltar la cobertura de código Apex) está habilitada.

El motivo por el que la línea 5 no está cubierta es que nuestra clase de prueba no contenía ninguna prueba para pasar un parámetro de estado no válido. De forma similar, la línea 11 no está cubierta porque el método de prueba no ha pasado 'CA' como estado. Vamos a agregar dos métodos de prueba para cubrir estos casos. A continuación se muestra la clase de prueba completa después de agregar los métodos de prueba testTaskHighPriority() y testTaskPriorityInvalid(). Si vuelve a ejecutar esta clase de prueba, la cobertura de código para TaskUtil es del 100%.

@IsTest
private with sharing class TaskUtilTest {
  @IsTest
  static void testTaskPriority() {
    String pri = TaskUtil.getTaskPriority('NY');
    Assert.areEqual('Normal', pri);
  }
  @IsTest
  static void testTaskHighPriority() {
    String pri = TaskUtil.getTaskPriority('CA');
    Assert.areEqual('High', pri);
  }
  @IsTest
  static void testTaskPriorityInvalid() {
    String pri = TaskUtil.getTaskPriority('Montana');
    Assert.isNull(pri);
  }
}

Crear y ejecutar un conjunto de pruebas

Un conjunto de pruebas es una recopilación de clases de prueba de Apex que se ejecutan conjuntamente. Por ejemplo, cree un conjunto de pruebas que ejecute cada vez que prepare una implementación o que Salesforce lance una nueva versión. Configure un conjunto de pruebas en la consola de desarrollador para definir un conjunto de clases de prueba que pueda ejecutar conjuntamente de forma regular.

Ahora tiene dos clases de prueba en su organización. Estas dos clases no están relacionadas, pero vamos a suponer de momento que sí lo están. Suponga que hay casos en los que desea ejecutar estas dos clases de prueba, pero no desea ejecutar todas las pruebas de la organización. Cree un conjunto de pruebas que contenga ambas clases y, a continuación, ejecute las pruebas del conjunto.

  1. En la Consola web, presione Ctrl+Shift+P (Windows) o ⌘+Shift+P (macOS) para abrir la Paleta de comandos.
  2. Ingrese SFDX: Create Apex Test Suite (SFDX: Crear conjunto de prueba de Apex) y presione Intro.
  3. Ingrese TempConverterTaskUtilSuite como nombre del conjunto y, luego, presione Intro.
  4. Seleccione TaskUtilTest y TemperatureConverterTest, y luego presione Intro.
  5. Haga clic en Icono de pruebas en la barra de actividades. Se abre el panel lateral Testing (Pruebas).
  6. En la barra de búsqueda Testing (Pruebas), ingrese TempConverterTaskUtilSuite.
  7. Para ejecutar todas las pruebas en el conjunto de pruebas, pase el cursor sobre la línea TempConverterTaskUtilSuite y ícono Run Test Suite.

Crear datos de prueba

Los registros de Salesforce creados en métodos de prueba no se confirman para la base de datos. Estos se revierten una vez que finaliza la ejecución de la prueba. Este comportamiento de reversión es de utilidad para las pruebas porque no necesita limpiar los datos de prueba después de ejecutar la prueba.

De forma predeterminada, las pruebas de Apex no tienen acceso a los datos preexistentes de la organización, a excepción del acceso a objetos de configuración y metadatos, como los objetos de usuario o perfil. Configure los datos de prueba para las pruebas. La creación de datos de prueba aumenta la consistencia de las pruebas y permite evitar errores generados por la ausencia o el cambio de los datos de la organización. Puede crear datos de prueba directamente en el método de prueba o mediante una clase de prueba de utilidad, como verá más adelante.

Note

Aunque no es una mejor práctica, hay ocasiones en las que un método de prueba necesita acceso a los datos preexistentes. Para acceder a los datos de la organización, anote el método de prueba con @IsTest(SeeAllData=true). Los métodos de prueba de ejemplo de esta unidad no tienen acceso a los datos de la organización y, por lo tanto, no usan el parámetro SeeAllData.

Más información

  • Puede usar la extensión de Apex de Salesforce para Visual Studio Code a fin de ejecutar pruebas de Apex y verificar la funcionalidad de su código.
  • Puede ahorrar hasta 6 MB de código Apex en cada organización. Las clases de prueba anotadas con @IsTest no cuentan para este límite.
  • Aunque los datos de prueba se revierten, no se usa ninguna base de datos independiente para las pruebas. Por lo tanto, en el caso de determinados sObjects que tienen campos con restricciones de exclusividad, la inserción de registros de sObject duplicados genera un error.
  • Los métodos de prueba no permiten el envío de emails.
  • Los métodos de prueba no pueden hacer llamadas a servicios externos. Puede usar llamadas ficticias en las pruebas.
  • Las búsquedas SOSL realizadas en una prueba devuelven resultados vacíos. Para garantizar los resultados esperados, use Test.setFixedSearchResults() para definir los registros que va a devolver la búsqueda.

Recursos

Preparación para el reto práctico

Para completar el desafío práctico de esta unidad, deberá crear una nueva clase de Appex denominada VerifyDate con el siguiente código:

public with sharing class VerifyDate {
  //method to handle potential checks against two dates
  public static Date CheckDates(Date date1, Date date2) {
    //if date2 is within the next 30 days of date1, use date2.  Otherwise use the end of the month
    if(DateWithin30Days(date1,date2)) {
      return date2;
    } else {
      return SetEndOfMonthDate(date1);
    }
  }
  //method to check if date2 is within the next 30 days of date1
  private static Boolean DateWithin30Days(Date date1, Date date2) {
    //check for date2 being in the past
    if( date2 < date1) { return false; }
    //check that date2 is within (>=) 30 days of date1
    Date date30Days = date1.addDays(30); //create a date 30 days away from date1
    if( date2 >= date30Days ) { return false; }
    else { return true; }
  }
  //method to return the end of the month of a given date
  private static Date SetEndOfMonthDate(Date date1) {
    Integer totalDays = Date.daysInMonth(date1.year(), date1.month());
    Date lastDay = Date.newInstance(date1.year(), date1.month(), totalDays);
    return lastDay;
  }
}
Comparta sus comentarios de Trailhead en la Ayuda de Salesforce.

Nos encantaría saber más sobre su experiencia con Trailhead. Ahora puede acceder al nuevo formulario de comentarios en cualquier momento en el sitio de Ayuda de Salesforce.

Más información Continuar a Compartir comentarios