web-dev-qa-db-fra.com

Comment effectuer un test unitaire avec ActionResult <T>?

J'ai un test xUnit comme:

[Fact]
public async void GetLocationsCountAsync_WhenCalled_ReturnsLocationsCount()
{
    _locationsService.Setup(s => s.GetLocationsCountAsync("123")).ReturnsAsync(10);
    var controller = new LocationsController(_locationsService.Object, null)
    {
        ControllerContext = { HttpContext = SetupHttpContext().Object }
    };
    var actionResult = await controller.GetLocationsCountAsync();
    actionResult.Value.Should().Be(10);
    VerifyAll();
}

La source est

/// <summary>
/// Get the current number of locations for a user.
/// </summary>
/// <returns>A <see cref="int"></see>.</returns>
/// <response code="200">The current number of locations.</response>
[HttpGet]
[Route("count")]
public async Task<ActionResult<int>> GetLocationsCountAsync()
{
    return Ok(await _locations.GetLocationsCountAsync(User.APropertyOfTheUser()));
}

La valeur du résultat est nulle, ce qui entraîne l'échec de mon test, mais si vous regardez ActionResult.Result.Value (une propriété interne), il contient la valeur résolue attendue.

Voir la capture d'écran suivante du débogueur. enter image description here

Comment puis-je obtenir l'actionResult.Value à remplir dans un test unitaire?

14
Richard Collette

Au moment de l'exécution, votre code d'origine testé fonctionnerait toujours en raison de la conversion implicite.

Mais sur la base de l'image du débogueur fournie, il semble que le test ait affirmé la mauvaise propriété du résultat.

Ainsi, tout en changeant la méthode sous test a permis au test de passer, cela aurait fonctionné lors de l'exécution en direct dans les deux sens

ActioResult<TValue> A deux propriétés qui sont définies en fonction de ce qui est retourné par l'action qui l'utilise.

/// <summary>
/// Gets the <see cref="ActionResult"/>.
/// </summary>
public ActionResult Result { get; }

/// <summary>
/// Gets the value.
/// </summary>
public TValue Value { get; }

Source

Ainsi, lorsque l'action du contrôleur est retournée à l'aide de Ok(), elle définit la propriété ActionResult<int>.Result Du résultat de l'action via une conversion implicite.

public static implicit operator ActionResult<TValue>(ActionResult result)
{
    return new ActionResult<TValue>(result);
}

Mais le test affirmait la propriété Value (voir l'image dans OP), qui dans ce cas n'était pas définie.

Sans avoir à modifier le code testé pour satisfaire le test, il aurait pu accéder à la propriété Result et faire des assertions sur cette valeur

[Fact]
public async Task GetLocationsCountAsync_WhenCalled_ReturnsLocationsCount() {
    //Arrange
    _locationsService
        .Setup(_ => _.GetLocationsCountAsync(It.IsAny<string>()))
        .ReturnsAsync(10);
    var controller = new LocationsController(_locationsService.Object, null) {
        ControllerContext = { HttpContext = SetupHttpContext().Object }
    };

    //Act
    var actionResult = await controller.GetLocationsCountAsync();

    //Assert
    var result = actionResult.Result as OkObjectResult;
    result.Should().NotBeNull();
    result.Value.Should().Be(10);

    VerifyAll();
}
6
Nkosi

Le problème est de l'envelopper dans Ok. Si vous renvoyez l'objet lui-même, Value est correctement rempli.

Si vous regardez exemples de Microsoft dans les documents , ils utilisent uniquement les méthodes de contrôleur pour les réponses non par défaut comme NotFound:

[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById(int id)
{
    if (!_repository.TryGetProduct(id, out var product))
    {
        return NotFound();
    }

    return product;
}
0
Eric Eskildsen