0Pricing
C# Academy · Aula

Depuração de testes que falham

Torne as falhas reproduzíveis, remova tempo e aleatoriedade, reduza o caso a um exemplo mínimo e melhore as mensagens das asserções para um diagnóstico rápido.

Depuração de testes que falham é uma aula grátis de C# Academy no CoddyKit. Esta é a aula 3 de 3. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de C# Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de C# Academy inclui 3 aulas no total.

Plano de ação

Plano para corrigir testes com falha:

  • Reproduza o problema de forma confiável
  • Remova a instabilidade (tempo/E/S aleatórios)
  • Reduza até chegar ao menor caso com falha
  • Adicione mensagens claras nas asserções

Instável por projeto

Usar UtcNow e o Random padrão faz o comportamento variar entre as execuções → testes instáveis.

using System;

// Demo: occasionally fails because it depends on current second and default Random()
public static class Promo
{
  // Gives bonus on even seconds; random adds small jitter
  public static int BonusNow()
  {
    int baseBonus = (DateTime.UtcNow.Second % 2 == 0) ? 10 : 9;
    Random r = new Random(); // time-based seed → unpredictable
    return baseBonus + (r.Next(0, 2)); // 0 or 1 more
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    // Simulated test: sometimes expects exactly 10, but result varies
    int got = Promo.BonusNow();
    if (got != 10) Console.WriteLine("FAIL (flaky): expected 10 but got " + got);
    else Console.WriteLine("PASS (maybe): got " + got);
  }
}

Controlar dependências

Injete o tempo e Random. Com uma semente fixa e um carimbo de data e hora fixo, a falha se torna reproduzível.

using System;

public interface IClock { DateTime NowUtc(); }
public sealed class FakeClock : IClock
{
  private readonly DateTime _fixedUtc;
  public FakeClock(DateTime fixedUtc) { _fixedUtc = fixedUtc; }
  public DateTime NowUtc() { return _fixedUtc; }
}

public sealed class PromoDeterministic
{
  private readonly IClock _clock;
  private readonly Random _rng;
  public PromoDeterministic(IClock clock, Random rng) { _clock = clock; _rng = rng; }

  public int BonusNow()
  {
    int baseBonus = (_clock.NowUtc().Second % 2 == 0) ? 10 : 9;
    return baseBonus + _rng.Next(0, 2);
  }
}

public static class Assert
{
  public static void AreEqual(int expected, int actual, string msg)
  {
    if (expected != actual) throw new Exception("Expected " + expected + " but got " + actual + " :: " + msg);
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    try
    {
      IClock clock = new FakeClock(new DateTime(2016, 1, 1, 0, 0, 20, DateTimeKind.Utc)); // even second
      Random rng = new Random(123); // fixed seed
      PromoDeterministic p = new PromoDeterministic(clock, rng);

      int got = p.BonusNow(); // predictable now
      Assert.AreEqual(10, got, "even second, first draw should be 0");
      Console.WriteLine("PASS: deterministic promo");
    }
    catch (Exception ex)
    {
      Console.WriteLine("FAIL: " + ex.Message);
    }
  }
}

Reprodução mínima

Reduza as entradas até que um exemplo pequeno falhe (aqui: uma matriz vazia). Casos pequenos são mais fáceis de analisar.

using System;

// Buggy function: average of ints, but divides by count without guarding empty input
public static class Stats
{
  public static double Average(int[] xs)
  {
    int sum = 0;
    for (int i = 0; i < xs.Length; i++) sum += xs[i];
    return sum / xs.Length; // BUG: divide by zero if empty; also int division truncates
  }
}

public static class Assert
{
  public static void Throws<T>(Action a, string msg) where T : Exception
  {
    try { a(); }
    catch (Exception ex)
    {
      if (ex is T) return;
      throw new Exception("Expected " + typeof(T).Name + " but got " + ex.GetType().Name + " :: " + msg);
    }
    throw new Exception("Expected " + typeof(T).Name + " but no exception thrown :: " + msg);
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    // Minimal failing case: empty array
    Assert.Throws<DivideByZeroException>(delegate { Stats.Average(new int[0]); }, "empty input");
    Console.WriteLine("Reproduced: empty input triggers the bug.");
  }
}

Qualidade da correção e das mensagens

Proteja-se contra entradas inválidas e use mensagens de asserção descritivas (inclua entradas, valor esperado e épsilon).

using System;

public static class Stats
{
  public static double Average(int[] xs)
  {
    if (xs == null) throw new ArgumentNullException("xs");
    if (xs.Length == 0) throw new ArgumentException("xs must not be empty", "xs");

    double sum = 0.0; // use double to avoid integer truncation
    for (int i = 0; i < xs.Length; i++) sum += xs[i];
    return sum / xs.Length;
  }
}

public static class Assert
{
  public static void AreAlmostEqual(double expected, double actual, double eps, string msg)
  {
    if (Math.Abs(expected - actual) > eps)
      throw new Exception("Expected " + expected + " but got " + actual + " (eps " + eps + ") :: " + msg);
  }
}

public class Program
{
  public static void Main(string[] args)
  {
    try
    {
      double a = Stats.Average(new int[] { 2, 4, 6 });
      Assert.AreAlmostEqual(4.0, a, 1e-9, "average of 2,4,6");

      Console.WriteLine("PASS: fixed average with clear message");
    }
    catch (Exception ex)
    {
      Console.WriteLine("FAIL: " + ex.Message);
    }
  }
}

Lista de verificação da depuração

Lista de verificação da depuração:

  • Estabilize as dependências (tempo, aleatoriedade, E/S)
  • Reproduza localmente e repetidamente
  • Reduza até chegar a uma entrada mínima que falhe
  • Adicione mensagens precisas nas asserções
  • Prefira funções puras e verificações de proteção

Primeiro passo para testes instáveis

Verificação rápida: um teste que falha de forma intermitente usa DateTime.UtcNow e new Random(). Qual é o melhor primeiro passo?

Recapitulação

Recapitulação: reproduza o problema de forma confiável controlando o tempo e a aleatoriedade, reduza a entrada e escreva mensagens úteis nas asserções para localizar rapidamente o erro.

Perguntas Frequentes

A aula “Depuração de testes que falham” é grátis?

Sim — o texto completo de “Depuração de testes que falham” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de C# Academy, atualize para CoddyKit PRO. O curso de C# Academy inclui 3 aulas no total.

O que vou aprender em “Depuração de testes que falham”?

Torne as falhas reproduzíveis, remova tempo e aleatoriedade, reduza o caso a um exemplo mínimo e melhore as mensagens das asserções para um diagnóstico rápido. Você pratica C# Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar C# Academy?

Nenhuma experiência prévia é necessária. C# Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 3.

Quanto tempo leva a aula “Depuração de testes que falham”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de C# Academy?

Sim. Cada aula de C# Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. Configuração e asserções de xUnit/MSTest
  2. Falsificações por meio de interfaces; criadores de dados de teste
  3. Depuração de testes que falham
← Voltar para C# Academy