web-dev-qa-db-fra.com

Comment obtenir les valeurs d'une tâche <IActionResult> renvoyée via une API pour les tests unitaires

J'ai créé une API à l'aide d'ASP.NET MVC Core v2.1. Une de mes méthodes HttpGet est configurée comme suit:

public async Task<IActionResult> GetConfiguration([FromRoute] int? id)
{
    try
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        ..... // Some code here

        return Ok(configuration);
    }
    catch (Exception ex)
    {
        ... // Some code here
    }
}

Lors des tests unitaires, je peux vérifier que Ok était la réponse, mais j'ai vraiment besoin de voir les valeurs de la configuration. Il semble que je ne sois pas capable de faire fonctionner ceci avec les éléments suivants:

[TestMethod] 
public void ConfigurationSearchGetTest()
{
    var context = GetContextWithData();
    var controller = new ConfigurationSearchController(context);
    var items = context.Configurations.Count();
    var actionResult = controller.GetConfiguration(12);

    Assert.IsTrue(true);
    context.Dispose();
}

Au moment de l'exécution, je peux vérifier que actionResult a certaines valeurs pour lesquelles je ne parviens pas à coder. Y a-t-il quelque chose que je fais mal? Ou suis-je simplement en train de penser à cela? J'aimerais pouvoir faire:

Assert.AreEqual(12, actionResult.Values.ConfigurationId);

Les bonnes pratiques suggèrent de ne pas avoir beaucoup de code dans les actions de votre contrôleur à tester et que la majeure partie de la logique se trouve dans des objets découplés ailleurs qui sont beaucoup plus faciles à tester. Cela dit, si vous souhaitez toujours tester vos contrôleurs, vous devez effectuer votre test async et attendre les appels.

L’un des problèmes que vous aurez, c’est que vous utilisez IActionResult car cela vous permet de renvoyer BadRequest(...) et Ok(...). Cependant, étant donné que vous utilisez ASP.NET MVC Core 2.1, vous souhaiterez peut-être commencer par utiliser le nouveau type ActionResult<T> . Cela devrait vous aider dans vos tests car vous pouvez désormais accéder directement à la valeur de retour fortement typée. Par exemple:

//Assuming your return type is `Configuration`
public async Task<ActionResult<Configuration>> GetConfiguration([FromRoute] int? id)
{
    try
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        ..... // Some code here

        // Note we are now returning the object directly, there is an implicit conversion 
        // done for you
        return configuration;
    }
    catch (Exception ex)
    {
        ... // Some code here
    }
}

Notez que nous retournons maintenant l'objet directement car il existe une conversion implicite de Foo à ActionResult<Foo>

Votre test peut maintenant ressembler à ceci:

[TestMethod] 
public async Task ConfigurationSearchGetTest()
{
    var context = GetContextWithData();
    var controller = new ConfigurationSearchController(context);
    var items = context.Configurations.Count();

    // We now await the call
    var actionResult = await controller.GetConfiguration(12);

    // And the value we want is now a property of the return
    var configuration = actionResult.Value;

    Assert.IsTrue(true);
    context.Dispose();
}
6
DavidG

Vous devez attendre l'appel de GetConfiguration pour récupérer l'objet IActionResult comme suit:

var actionResult = await controller.GetConfiguration(12);

Pour ce faire, vous devez également modifier la signature de votre méthode de test de manière asynchrone. Alors changez ceci:

public void ConfigurationSearchGetTest()

Pour ça:

public async Task ConfigurationSearchGetTest()
1
Baffour

Comme ma réputation ne me permet pas de commenter la réponse de @DavidG, qui va dans la bonne direction, je vais vous donner un exemple pour obtenir la valeur dans Task .

Comme @ Christopher J. Reynolds l'a fait remarquer actionResult.Value peut être vu à runtime mais pas dans le temps de compilation .

Donc, je vais montrer un test de base dans lequel obtenir les valeurs:

[TestMethod]
    public async Task Get_ReturnsAnArea()
    {
        // Arrange
        string areaId = "SomeArea";
        Area expectedArea = new Area() { ObjectId = areaId, AreaNameEn = "TestArea" };

        var restClient = new Mock<IRestClient>();
        restClient.Setup(client => client.GetAsync<Area>(It.IsAny<string>(), false)).ReturnsAsync(expectedArea);

        var controller = new AreasController(restClient.Object);

        //// Act

        // We now await the call
        IActionResult actionResult = await controller.Get(areaId);

        // We Cast it to the expected Response Type
        OkObjectResult okResult = actionResult as OkObjectResult;

        // Assert

        Assert.IsNotNull(okResult);
        Assert.AreEqual(200, okResult.StatusCode);

        Assert.AreEqual(expectedArea, okResult.Value);

       //We Cast Value to the expected Type
        Area actualArea = okResult.Value as Area;
        Assert.IsTrue(expectedArea.AreaNameEn.Equals(actualArea.AreaNameEn));
    }

Bien sûr, cela pourrait être amélioré, mais je voulais juste vous montrer un moyen simple de le comprendre. J'espère que cela vous aidera.

0
sebainones

Vous pouvez obtenir un contrôleur testé sans changer le type renvoyé.
IActionResult est le type de base pour tous les autres.
Convertit le résultat dans le type attendu et compare la valeur renvoyée à la valeur attendue.

Puisque vous testez une méthode asynchrone, rendez-la également asynchrone.

[TestMethod] 
public async Task ConfigurationSearchGetTest()
{
    using (var context = GetContextWithData())
    {
        var controller = new ConfigurationSearchController(context);
        var items = context.Configurations.Count();

        var actionResult = await controller.GetConfiguration(12);

        var okResult = actionResult as OkObjectResult;
        var actualConfiguration = okResult.Value as Configuration:

        // Now you can compare with expected values
        actualConfuguration.Should().BeEquivalentTo(expected);
    }
}
0
Fabio