Introdução aos testes de unidade do Apex
Objetivos de aprendizagem
Após concluir esta unidade, você estará apto a:
- Descrever os principais benefícios dos testes de unidade do Apex.
- Definir uma classe com métodos de teste.
- Executar todos os métodos de teste de uma classe e inspecionar falhas.
- Criar e executar um conjunto de classes de teste.
Testes de unidade do Apex
A estrutura de testes do Apex permite que você escreva e execute testes para suas classes e acionadores do Apex. Os testes de unidade do Apex garantem alta qualidade para seu código do Apex e lhe permitem cumprir os requisitos para implantação do Apex.
O teste é o segredo do desenvolvimento bem-sucedido em longo prazo e um componente essencial do processo de desenvolvimento. Com a estrutura de testes do Apex é mais fácil testar seu código do Apex. O código do Apex só pode ser escrito em uma sandbox ou em uma organização do desenvolvedor, e não em uma organização de produção. O código do Apex pode ser implantado em uma organização de produção a partir de um sandbox. Além disso, os desenvolvedores de aplicativos podem distribuir o código do Apex aos clientes de suas organizações de desenvolvedores carregando os pacotes para o Salesforce AgentExchange. Além de serem fundamentais para a garantia de qualidade, os testes de unidade do Apex também são requisitos para implantar e distribuir o Apex.
Os testes de unidade do Apex resultam em códigos aprimorados. A estrutura de testes do Apex:
- Garante que suas classes e acionadores do Apex funcionem conforme previsto.
- Tem um conjunto de testes de regressão que pode ser executado novamente sempre que as classes e os acionadores forem atualizados a fim de garantir que as futuras atualizações que você vier a fazer ao seu aplicativo não interrompam a funcionalidade existente.
- Cumpre os requisitos de cobertura de código para a implantação do Apex na produção ou distribuição do Apex a clientes por meio de pacotes.
- Oferece aplicativos de alta qualidade à organização de produção, o que torna os usuários de produção mais produtivos.
- Oferece aplicativos de alta qualidade aos assinantes de pacotes, o que aumenta a confiança de seus clientes.
Requisito de cobertura de código para implantação
Antes de implantar seu código ou empacotá-lo no Salesforce AgentExchange, pelo menos 75% do código do Apex deve ser coberto por testes e todos esses testes devem ser aprovados. Além disso, cada acionador deve ter uma cobertura. Embora a cobertura de código seja um requisito para a implantação, não escreva testes apenas para atender a essa exigência. Teste os casos de uso comuns em seu aplicativo, incluindo casos positivos e negativos do teste, bem como o processamento em massa ou de um só registro.
Classe de teste e sintaxe do método de teste
As classes de teste do Apex com a anotação @IsTest. As classes de teste podem ser definidas como modificadores de acesso privados ou públicos. Se você estiver usando uma classe de teste apenas para testes de unidade, declare-a como privada. As classes de teste públicas geralmente são usadas em classes de fábrica de dados de teste, que abordaremos na unidade Criar dados de teste para testes do Apex. As classes definidas como @IsTest devem ser classes de nível superior.
Na classe de teste, os métodos de teste também são definidos usando a anotação @IsTest. A visibilidade de um método de teste não importa, por isso, declarar um método de teste como público ou privado não faz diferença, pois a estrutura de testes é sempre capaz de acessar métodos de teste. Por essa razão, os modificadores de acesso são omitidos na sintaxe do método.
Este exemplo de código mostra uma definição de uma classe de teste com um método de teste.
@IsTest
private with sharing class MyTestClass {
@IsTest static void myTest() {
// code_block
}
}A anotação @IsTest pode utilizar vários modificadores entre parênteses e separados por espaços. Abordaremos brevemente a anotação @IsTest(seeAllData=true) na unidade Criar dados de teste para testes do Apex.
Exemplo de teste de unidade: Testar a classe TemperatureConverter
O exemplo simples a seguir pertence a uma classe de teste com três métodos de teste. O método de classe que está sendo testado possui uma temperatura em Fahrenheit como entrada. Ele converte essa temperatura para Celsius e informa o resultado convertido. Vamos adicionar a classe personalizada e sua classe de teste.
- Abra o Web Console no menu de acesso rápido (
).
- No Web Console, pressione Ctrl+Shift+P (Windows) ou ⌘+Shift+P (macOS) para abrir a paleta de comandos.
- Insira
SFDX: Create Apex Classe pressione Enter.
- Para o tipo de modelo, selecione DefaultApexClass e pressione Enter.
- Como nome da classe, insira
TemperatureConvertere pressione Enter.
- Para o diretório de saída, selecione force-app/main/default/classes e pressione Enter.
- Substitua o corpo de classe padrão pelo seguinte.
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);
}
}- Para salvar e implantar sua classe, pressione Ctrl+S (Windows) ou ⌘+S (macOS).
- Repita as etapas anteriores para criar a classe
TemperatureConverterTest. Substitua o corpo de classe padrão pelo seguinte.@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); } }
A classe de teste TemperatureConverterTest verifica se o método funciona conforme previsto, chamando-o com diferentes entradas para a temperatura em Fahrenheit. Cada método de teste verifica um tipo de entrada: uma temperatura quente, a temperatura do ponto de congelamento, a temperatura do ponto de ebulição e uma temperatura negativa. As verificações são feitas chamando o método Assert.areEqual() que recebe dois parâmetros. O primeiro parâmetro é o valor esperado e o segundo é o valor real. Este método também pode aceitar um terceiro parâmetro, que é uma cadeia de caracteres que descreve a comparação que está sendo feita. Este terceiro parâmetro opcional é usado no exemplo testBoilingPoint(). A cadeia de caracteres especificada está registrada caso a declaração falhe.
Habilitar resultados de cobertura de código
Antes de executar testes, habilite o Web Console para recuperar os resultados de cobertura de código.
- No Web Console, clique em
na parte superior da barra de atividades.
- Selecione File (Arquivo) | Preferences (Preferências) | Settings (Configurações) para abrir a janela Settings (Configurações).
- Na caixa de pesquisa, insira
retrieve.
- Habilite a configuração Salesforcedx-vscode-apex-testing: Retrieve-test-code-coverage.
- Feche a janela.
Agora vamos executar os métodos nesta classe de teste.
- No Web Console, clique em
na barra de atividades. O painel lateral Testing (Testes) é aberto.
- Na barra de pesquisa Testing (Testes), insira
TemperatureConverterTest.
- Para executar todos os métodos de teste na classe, passe o cursor sobre a linha TemperatureConverterTest e clique em
.
No painel Test Results (Resultados dos testes), você vê os resultados dos seus testes. No painel lateral Testing (Testes), os métodos de teste agora exibem marcas de verificação verdes, indicando que todos os testes foram aprovados. O Web Console também gera arquivos JSON e Markdown dos resultados dos testes, localizados no diretório .sfdx/tools/testresults/apex.

Depois de executar os testes, a cobertura de código é gerada automaticamente para as classes e acionadores do Apex presentes na organização. Você pode verificar o percentual de cobertura de código no arquivo Markdown dos resultados dos testes. Neste exemplo, a classe TemperatureConverter tem uma cobertura de 100%, conforme mostrado nesta imagem.

Embora um método de teste teria resultado em uma cobertura total da classe TemperatureConverter, ainda é importante testar diferentes entradas para garantir a qualidade do seu código. Obviamente, não é possível verificar todos os pontos de dados, mas você pode realizar testes em pontos de dados comuns e diferentes intervalos de entrada. Por exemplo, você pode verificar a aprovação de números positivos e negativos, valores-limite e valores de parâmetros inválidos para verificar o comportamento negativo. Os testes para a classe TemperatureConverter verificam os pontos de dados comuns, como pontos de congelamento e ebulição, e temperaturas positivas e negativas.
No entanto, a classe de teste TemperatureConverterTest atualmente não abrange condições-limite ou entradas inválidas. As condições-limite são os valores mínimo e máximo que o método é capaz de processar. No caso das entradas inválidas, considere o que ocorre se null for passado como argumento para FahrenheitToCelsius(). Nesse caso, quando o tempo de execução do Apex desreferencia a variável do parâmetro para avaliar a fórmula, ele lança um System.NullPointerException. Para tratar esse erro, você pode modificar o método FahrenheitToCelsius() para verificar se há uma entrada inválida e, nesse caso, retornar null. Em seguida, adicione um método de teste à classe TemperatureConverterTest para verificar o comportamento da entrada inválida.
Até este ponto, todos os testes são aprovados porque a fórmula de conversão utilizada no método de classe é correta. Mas isso é aborrecido! Tentaremos simular uma falha apenas para ver o que acontece quando uma declaração falha. Por exemplo, vamos modificar o teste de temperatura de ponto de ebulição e aprovar um valor falso previsto para a temperatura de ponto de ebulição em graus Celsius (0 em vez de 100). Isso faz com que o método de teste correspondente falhe.
- Altere o método de teste
testBoilingPoint()para o código a seguir e salve a classe de teste atualizada.
@IsTest
static void testBoilingPoint() {
Decimal celsius = TemperatureConverter.FahrenheitToCelsius(212);
// Simulate failure
Assert.areEqual(0,celsius,'Boiling point temperature is not expected.');
}- No painel lateral Testing (Testes), execute todos os métodos de teste na classe TemperatureConverterTest. A declaração em
testBoilingPoint()falha e gera um erro fatal (umaAssertExceptionque não pode ser capturada).
- Verifique os resultados no painel Test Results (Resultados dos testes). A execução de teste informa a falha de um em cada quatro testes.
- Para obter a mensagem de erro da falha do teste, selecione testBoilingPoint no painel lateral Testing (Testes). A mensagem de erro inclui o texto que fornecemos na instrução
Assert.areEqual():System.AssertException: Assertion Failed: Boiling point temperature is not expected.: Expected: 0, Actual: 100.00(System.AssertException: Falha na declaração: A temperatura de ponto de ebulição não é esperada.:Esperada: 0, Real: 100.00)

Os dados de teste nesses métodos de teste são números e não registros do Salesforce. Na próxima unidade, você aprenderá mais sobre como testar os registros do Salesforce e como configurar seus dados.
Aumente sua cobertura de código
Ao escrever testes, tente alcançar a maior cobertura de código possível. Não basta apontar para uma cobertura de 75%, que é a menor cobertura que o Salesforce exige para implantações e pacotes. Quanto mais casos de teste seus testes cobrirem, maior a probabilidade de o seu código ser robusto. Às vezes, mesmo depois de escrever métodos de teste para todos os seus métodos de classe, a cobertura de código não chega a 100%. Uma causa comum é não cobrir todos os valores de dados referentes à execução de código condicional. Por exemplo, alguns valores de dados tendem a ser ignorados quando seu método de classe possui instruções if que fazem com que diferentes ramificações sejam executadas desde que a condição avaliada seja atendida. Garanta que seus métodos de teste tenham em consideração esses valores diferentes.
Esse exemplo inclui o método de classe getTaskPriority(), que contém duas instruções if. A principal tarefa deste método é retornar um valor de string prioritário com base no estado de lead informado. O método valida primeiramente o estado e retorna null se o estado for inválido. Se o estado for CA, o método retorna 'High' (Alto); caso contrário, retorna 'Normal' para qualquer outro 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;
}
}Esta é a classe de teste para o método getTaskPriority(). O método de teste simplesmente chama getTaskPriority() com um estado (NY).
@IsTest
private with sharing class TaskUtilTest {
@IsTest
static void testTaskPriority() {
String pri = TaskUtil.getTaskPriority('NY');
Assert.areEqual('Normal', pri);
}
}Vamos executar esta classe de teste (TaskUtilTest) no Web Console e verificar a cobertura de código para a classe TaskUtil correspondente que este teste cobre. Após o término da execução do teste, a cobertura de código para TaskUtil é mostrada como 75%.
Agora abra o arquivo TaskUtil.cls. Com esse arquivo aberto, clique em
na parte inferior da janela do Web Console para habilitar a configuração Highlight Apex Code Coverage (Destacar cobertura de código do Apex). Com essa configuração habilitada, você verá seis linhas verdes (cobertas) e duas linhas vermelhas (não cobertas) na classe TaskUtil, conforme mostrado nesta imagem.

A linha 5 não está coberta porque nossa classe de teste não contém um teste para aprovar um parâmetro de estado inválido. Da mesma forma, a linha 11 não está coberta porque o método de teste não recebeu 'CA' como estado. Vamos adicionar mais dois métodos de teste para cobrir esses cenários. A seguir, apresentamos a classe de teste completa após adicionarmos os métodos de teste testTaskHighPriority() e testTaskPriorityInvalid(). Se executar novamente essa classe de teste, a cobertura de código para TaskUtil agora está em 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);
}
}Criar e executar um conjunto de testes
Um conjunto de testes é um conjunto de classes de teste do Apex que você executa juntas. Por exemplo, crie um conjunto de testes para ser executado sempre que você preparar uma implantação ou que a Salesforce liberar uma nova versão. Configure um conjunto de testes no Developer Console para definir um conjunto de classes de teste que são executadas juntas regularmente.
Agora você tem duas classes de teste em sua organização. Essas duas classes não estão relacionadas, mas por ora vamos fingir que estão. Suponhamos que haja situações em que você deseja executar essas duas classes de teste, mas não deseja executar todos os testes em sua organização. Crie um conjunto de testes que contenha ambas as classes e, em seguida, execute os testes no conjunto.
- Em Web Console (Console da Web), pressione Ctrl+Shift+P (Windows) ou ⌘+Shift+P (macOS) para abrir a paleta de comandos.
- Insira
SFDX: Create Apex Test Suitee pressione Enter.
- Insira
TempConverterTaskUtilSuitecomo nome do conjunto e pressione Enter.
- Selecione TaskUtilTest e TemperatureConverterTest e pressione Enter.
- Clique em
na barra de atividades. O painel lateral Testing (Testes) é aberto.
- Na barra de pesquisa Testing (Testes), insira
TempConverterTaskUtilSuite.
- Para executar todos os testes no conjunto de testes, passe o cursor sobre a linha TempConverterTaskUtilSuite e clique em
.
Criar dados de teste
Os registros do Salesforce criados em métodos de teste não estão comprometidos com o banco de dados. Eles são revertidos quando a execução do teste é concluída. Esse comportamento de reversão é útil para testes porque você não tem que limpar seus dados de teste após o teste ser executado.
Por padrão, os testes do Apex não têm acesso aos dados preexistentes na organização, exceto para acesso a objetos de configuração e metadados, tais como os objetos Usuário ou Perfil. Configure os dados de teste dos seus testes. A criação de dados de teste deixa seus testes mais robustos e evita falhas causadas pela falta de dados ou por dados alterados na organização. Você pode criar dados de teste diretamente em seu método de teste ou utilizando uma classe de utilitário de teste, como veremos mais tarde.
Quero saber mais
- Você pode usar a Extensão Apex do Salesforce para o Visual Studio Code para executar testes do Apex e verificar a funcionalidade do seu código.
- Você pode salvar até 6 MB de código do Apex em cada organização. As classes de teste anotadas com
@IsTestnão contam para esse limite.
- Embora os dados de teste sejam revertidos, nenhum banco de dados à parte é utilizado para testes. Como resultado, para alguns sObjects que têm campos com restrições exclusivas, a inserção de registros duplicados sObject resulta em um erro.
- Os métodos de teste não enviam emails.
- Os métodos de teste não podem fazer callouts para serviços externos. Você pode usar callouts simulados nos testes.
- As pesquisas SOSL realizadas em um teste retornam resultados vazios. Para garantir resultados previsíveis, utilize
Test.setFixedSearchResults()para definir os registros a serem retornados pela pesquisa.
Recursos
- Guia do desenvolvedor do Apex: Melhores práticas para teste
- Guia do desenvolvedor do Apex: O que são testes de unidade do Apex?
- Guia do desenvolvedor do Apex: Isolamento de dados de teste dos dados da organização em testes de unidade
- Ajuda do Salesforce: Verificar cobertura de código
Preparar-se para o desafio prático
Para completar o desafio prático desta unidade, você precisará criar uma nova classe do Apex chamada VerifyDate com o código copiado abaixo:
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;
}
}