Pruebas de integración en el limbo

A estas alturas, en pleno éxtasis de la programación asistida por agentes de inteligencia artificial, quién demonios lo hace a mano. Quién en su sano juicio se encarga de tipear un aburridísimo conjunto de pruebas unitarias y de integración. Yo, yo lo hago, y la semana antes de tomarme unos días de vacaciones me topé con un problema que ni ChatGPT en su nivel gratuito me pudo aclarar.

Mi objetivo era facilitarme la futura actualización de los casos en unas pruebas de integración en las que confirmo que distintos endpoint de un API me devuelven respuestas HTTP de tipo 401 y 403. Para lograrlo creé una clase estática a reutilizar como la fuente de los token de acceso que deben conducir a dichos resultados.

public static class Shared
{
  public static IEnumerable<TestCaseData> UnauthorizedTestCases()
  {
    // Sin JWT.
    yield return new TestCaseData("");
    // Con JWT vencido.
    yield return new TestCaseData("eyJhb...");
  }
}

Así cualquier prueba recibe el token, realiza su petición, obtiene su respuesta fallida y ¡listo! Mi sopresa fue que, cuando recién integré mi nuevo recurso, del total de casos sólo algunos se ejecutaban, en los restantes no había señal de éxito o error.

[Test]
[TestCaseSource(typeof(Shared), nameof(UnauthorizedTestCases))]
public async Task Get_ShouldReturnUnauthorizedResponse(string token)
{
  // 1. Arrange
  // 2. Act
  var actual = await HttpClient.GetAsync("v1/users", token: token);
  // 3. Assert
  Assert.That(actual.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
}

La situación no me resulta para nada extraña, me ha ocurrido antes y me suele provocar dolores de cabeza porque no siempre es sencillo encontrar el motivo. Por fortuna tuve la oportunidad de despejar la mente mientras resolvía tickets de prioridad. No es que me agrade interrumpir la tarea principal, pero en condiciones como esta representa un respiro bastante justo y necesario.

Descubrí entonces que la diferencia entre las pruebas que se ejecutaban y las que no radicaba en la extensión del token que era inyectado como parámetro. Para el motor de NUnit las cadenas de texto más cortas se procesan sin problema, pero las largas son omitidas. Desconozco la razón. Lo solucioné al envolver el token en una clase et voilà. A continuar con los demás pendientes.

public class TestCaseToken
{
  public string Name { get; private set; }
  public string Token { get; private set; }
  public TestCaseToken(string name, string token)
  {
    Name = name;
    Token = token;
  }
  public override string ToString() => Name;
}

La inclusión de la propiedad Name responde a la necesidad de pasar cierto contexto al explorador de pruebas para la mejor identificación de cada uno de los casos durante sesiones de depuración.

Código adicional actualizado
public static class Shared
{
  public static IEnumerable<TestCaseData> UnauthorizedTestCases()
  {
    yield return new TestCaseData(
      new TestCaseToken("Sin JWT", ""));
    yield return new TestCaseData(
      new TestCaseToken("Con JWT vencido", "eyJhb..."));
  }
}
[Test]
[TestCaseSource(typeof(Shared), nameof(UnauthorizedTestCases))]
public async Task Get_ShouldReturnUnauthorizedResponse(TestCaseToken testCase)
{
  // 1. Arrange
  // 2. Act
  var actual = await HttpClient.GetAsync("v1/users", token: testCase.Token);
  // 3. Assert
  Assert.That(actual.StatusCode, Is.EqualTo(HttpStatusCode.Unauthorized));
}

¿Algún comentario? Envíame un mensaje a moc.ff09e1@aloh.