Skip to main content

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

¿Su idioma de aprendizaje es español? Comience el reto en un Trailhead Playground en español y utilice las traducciones proporcionadas entre paréntesis para navegar. Copie y pegue solo los valores en inglés, ya que las validaciones del reto se basan en los datos en inglés. Si no aprueba el reto en su organización en español, le recomendamos que (1) cambie la configuración regional a Estados Unidos, (2) cambie el idioma a inglés, siga las instrucciones descritas aquí y, a continuación, (3) vuelva a hacer clic en el botón Check Challenge (Comprobar el reto).

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 pruebas de Apex permite escribir código y ejecutar pruebas para clases y desencadenadores de Apex en Lightning Platform. 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 solamente se puede escribir en un entorno de prueba o una organización de desarrollador, pero no de producción. El código Apex se puede implementar en una organización de producción desde un entorno de prueba. Además, los desarrolladores de aplicaciones pueden distribuir el código Apex entre los clientes desde sus organizaciones de desarrollador mediante la carga de paquetes a AppExchange de​Lightning Platform. 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. 

A continuación, se indican las ventajas de las pruebas de unidad de Apex.

  • Garantizar que las clases y los desencadenadores de Apex funcionen según lo esperado.
  • Disponer de un conjunto de pruebas de regresión que se puedan 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.
  • Cumplir 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.
  • Entrega de aplicaciones de alta calidad a la organización de producción, lo que aumenta la productividad de los usuarios de producción.
  • Entrega de aplicaciones de alta calidad a suscriptores de paquetes, lo que aumenta la confianza de los clientes.
Nota

Antes de cada actualización de servicio principal, Salesforce ejecuta todas las pruebas de Apex en su nombre a través de un proceso denominado Martillo de Apex. El proceso Hammer (Martillo) se ejecuta en la versión actual y la versión siguiente y compara los resultados de prueba, Este proceso garantiza que el comportamiento en su código personalizado no se altere como resultado de actualizaciones de servicios. El proceso Martillo elige organizaciones de forma selectiva y no se ejecuta en todas las organizaciones. Los problemas encontrados se clasifican basándose en algunos criterios. Salesforce se esfuerza en solucionar todos los problemas antes de cada versión nueva.

Mantener la seguridad de sus datos es nuestra máxima prioridad. No visualizamos o modificamos ningún dato en su organización y todas las pruebas se realizan en una copia que se ejecuta en un centro de datos seguro.

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

Para poder implementar su código o paquete para AppExchange de Lightning Platform, el 75 % del código Apex como mínimo debe estar cubierto por pruebas y todas deben haber sido aprobadas. 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.

Sintaxis de métodos de prueba

Los métodos de prueba se definen mediante la anotación @isTest y presentan la siguiente sintaxis:

@isTest static void testName() {

  // code_block

}

La anotación @isTest lleva varios modificadores entre paréntesis y separados por espacios en blanco. Describiremos uno de estos parámetros más adelante.

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.

Los métodos de prueba se deben definir en clases de prueba, las cuales son clases anotadas con @isTest. En esta clase de ejemplo se muestra una definición de una clase de prueba con un solo método de prueba.

@isTest

private class MyTestClass {

  @isTest static void myTest() {

    // code_block

  }

}

Las clases de prueba pueden ser privadas o públicas. 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 clases de fábrica de datos de prueba, lo cual se describe más adelante.

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. En Developer Console, haga clic en File (Archivo) | New (Nuevo) | Apex Class (Clase de Apex), escriba TemperatureConverter para el nombre de la clase y haga clic en OK (Aceptar).
  2. Sustituya el cuerpo de clase predeterminado por el siguiente.
    public 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);
      }
    }
  3. Pulse Ctrl+S para guardar la clase.
  4. Repita los pasos anteriores para crear la clase TemperatureConverterTest. Agregue lo siguiente para esta clase.
    @isTest
    private class TemperatureConverterTest {
      @isTest static void testWarmTemp() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(70);
        System.assertEquals(21.11,celsius);
      }
      @isTest static void testFreezingPoint() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(32);
        System.assertEquals(0,celsius);
      }
      @isTest static void testBoilingPoint() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(212);
        System.assertEquals(100,celsius,'Boiling point temperature is not expected.');
      }
      @isTest static void testNegativeTemp() {
        Decimal celsius = TemperatureConverter.FahrenheitToCelsius(-10);
        System.assertEquals(-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 de 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 mediante la llamada al método System.assertEquals() y se aplican dos parámetros: el primero es el valor esperado y el segundo es el valor real. Existe otra versión de este método en la que se aplica un tercer parámetro: una cadena que describe la comparación realizada, la cual se usa en testBoilingPoint(). Esta cadena opcional se registra si se produce un error de afirmación.

Vamos a ejecutar los métodos de esta clase.

  1. En Developer Console, haga clic en Test (Prueba) | (New Run) Nueva ejecución.
  2. En Test Classes (Clases de prueba), haga clic en TemperatureConverterTest.
  3. Para agregar todos los métodos de prueba de la clase TemperatureConverterTest en la ejecución de prueba, haga clic en Add Selected (Agregar selección).
  4. Haga clic en Run (Ejecutar).
  5. En la ficha Tests (Pruebas), puede ver el estado de las pruebas a medida que se ejecutan. Amplíe la ejecución de pruebas y vuelva a ampliarla hasta poder ver la lista de pruebas individuales ejecutadas. Todas estas pruebas incluyen marcas de verificación verdes. 
    Inspeccionar resultados de pruebas en Developer Console

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 comprobar el porcentaje de cobertura de código en la ficha Tests (Pruebas) de Developer Console. En este ejemplo, la clase probada (clase TemperatureConverter) tiene una cobertura del 100 %, como se muestra en la imagen.

Ver porcentaje de cobertura de código en Developer Console

Nota

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

Un problema conocido de Developer Console impide la actualización correcta de la cobertura de código al ejecutar un subconjunto de pruebas. Para actualizar todos los resultados de su cobertura de código, use Test (Prueba) | Run All (Ejecutar todo) en lugar de Test (Prueba) | New Run (Nueva ejecución).

Aunque un solo método de prueba puede dar 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 verifican puntos de datos comunes, como el punto de ebullición y los valores de temperatura negativos.

La clase de prueba TemperatureConverterTest no abarca las entradas no válidas ni las condiciones de límite. Las condiciones de límite están relacionadas con los valores mínimos y máximos. En este caso, el método de conversión de temperatura acepta un Decimal, lo que permite el uso de números altos y superiores a los valores Double. En el caso de las entradas no válidas, no existe ningún valor de temperatura no válido, sino que la única entrada no válida es null. ¿Cómo controla el método de conversión este valor? 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, se genera System.NullPointerException. Puede modificar el método FahrenheitToCelsius() para buscar una entrada no válida, devolver null si ese es el caso y, a continuación, agregar una prueba 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() por lo siguiente.
    @isTest
    static void testBoilingPoint() {
      Decimal celsius = TemperatureConverter.FahrenheitToCelsius(212);
      // Simulate failure
      System.assertEquals(0,celsius,'Boiling point temperature is not expected.');
    }
  2. Para ejecutar la misma ejecución de prueba, haga clic en la última ejecución en la ficha Tests (Pruebas) y, a continuación, haga clic en Test (Prueba) | Rerun (Volver a ejecutar). La afirmación de testBoilingPoint() no surte efecto y genera un error grave (excepción de tipo AssertException que no se puede capturar).
  3. Para comprobar los resultados de la ficha Tests (Pruebas), expanda la última prueba ejecutada. La ejecución de pruebas indica una de cuatro pruebas con errores. Para obtener más información sobre el fallo, haga doble clic en la prueba ejecutada. Los resultados detallados aparecerán en otra ficha, tal y como se muestra en esta imagen. 
    [Texto alternativo: Inspeccionar resultados de una prueba con errores en Developer Console]
  4. Para ver el mensaje de error de esta prueba incorrecta, haga doble clic en la columna de errores de dicha prueba. Verá lo siguiente. El texto descriptivo junto a Assertion Failed: (La afirmación ha fallado:) es el texto que incluimos en la declaración System.assertEquals().

    System.AssertException:Assertion Failed:Boiling point temperature is not expected.:Expected:0, Actual:100.00

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 Lightning Platform 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 producen 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 candidato 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 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;

  }

}
Nota

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 class TaskUtilTest {

  @isTest
  static void testTaskPriority() {

    String pri = TaskUtil.getTaskPriority('NY');

    System.assertEquals('Normal', pri);

  }

}

Vamos a ejecutar esta clase de prueba (TaskUtilTest) en Developer Console y a comprobar la cobertura de código para la clase TaskUtil correspondiente que abarca esta prueba. Una vez ejecutada la prueba, la cobertura de código para TaskUtil se indica como un 75 %. Si abre esta clase en Developer Console, verá seis líneas azules (cubiertas) y dos líneas rojas (no cubiertas), como se muestra en esta imagen.

Líneas cubiertas para la clase TaskUtil en Developer Console

El motivo por el que la línea 5 no se ha cubierto 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 se abarcó porque el método de prueba no pasó “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 mediante Run All (Ejecutar todo) o New Run (Nueva ejecución), la cobertura de código para TaskUtil será del 100 %.

@isTest

private class TaskUtilTest {

  @isTest

  static void testTaskPriority() {

    String pri = TaskUtil.getTaskPriority('NY');

    System.assertEquals('Normal', pri);

  }

  @isTest

  static void testTaskHighPriority() {

    String pri = TaskUtil.getTaskPriority('CA');

    System.assertEquals('High', pri);

  }

  @isTest

  static void testTaskPriorityInvalid() {

    String pri = TaskUtil.getTaskPriority('Montana');

    System.assertEquals(null, 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 Developer Console 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 Developer Console, seleccione Test (Prueba) | New Suite (Nuevo conjunto).
  2. Escriba TempConverterTaskUtilSuite como el nombre del conjunto y, a continuación, haga clic en OK (Aceptar).
  3. Seleccione TaskUtilTest, mantenga pulsada la tecla Ctrl y seleccione TemperatureConverterTest.
  4. Para agregar las clases seleccionadas al conjunto, haga clic en >Ventana de modificación del conjunto de pruebas con dos clases de prueba seleccionadas
  5. Haga clic en Save (Guardar).
  6. Seleccione Test (Prueba) | New Suite Run (Nueva ejecución de conjunto).
  7. Seleccione TempConverterTaskUtilSuite y, a continuación, haga clic en > para mover TempConverterTaskUtilSuite a la columna de conjuntos de prueba seleccionados.
  8. Haga clic en Run Suites (Ejecutar conjuntos).
  9. En la ficha Tests (Pruebas), puede ver el estado de las pruebas a medida que se ejecutan. Amplíe la ejecución de pruebas y vuelva a ampliarla hasta poder ver la lista de pruebas individuales ejecutadas. Al igual que en el caso de una ejecución de métodos de prueba individuales, puede hacer doble clic en los nombres de los métodos para ver los resultados detallados de las pruebas.

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.

Nota

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 utilizar la extensión de Apex de Salesforce para Visual Studio Code a fin de ejecutar pruebas de Apex y comprobar 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 correo electrónico.
  • 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() a fin de definir los registros que va a devolver la búsqueda.

Recursos

Prepárese para el reto práctico

Para poder completar el reto práctico de esta unidad, tendrá que crear una nueva clase de Apex denominada VerifyDate con el código copiado de aquí:

public 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 sobre Trailhead en la Ayuda de Salesforce.

Nos encantaría conocer su experiencia con Trailhead. Ahora puede acceder al nuevo formulario de comentarios cuando quiera desde el sitio de la Ayuda de Salesforce.

Más información Continuar para compartir comentarios