Affirmer une exception à l'aide de XUnit

111

Je suis un débutant sur XUnit et Moq. J'ai une méthode qui prend la chaîne comme argument. Comment gérer une exception en utilisant XUnit.

[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException() {
    //arrange
    ProfileRepository profiles = new ProfileRepository();
    //act
    var result = profiles.GetSettingsForUserID("");
    //assert
    //The below statement is not working as expected.
    Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}

Méthode sous test

public IEnumerable<Setting> GetSettingsForUserID(string userid)
{            
    if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id Cannot be null");
    var s = profiles.Where(e => e.UserID == userid).SelectMany(e => e.Settings);
    return s;
}
wandermonk
la source
1
Qu'entendez-vous par «ne fonctionne pas comme prévu»? (Veuillez également formater votre code de manière plus lisible. Utilisez l'aperçu et publiez quand il ressemble à ce que vous voudriez qu'il ressemble si vous le lisiez.)
Jon Skeet
4
Indice: vous appelez GetSettingsForUserID("")avant de commencer à appeler Assert.Throws. L' Assert.Throwsappel ne peut pas vous y aider. Je suggérerais d'être moins rigide à propos de AAA ...
Jon Skeet

Réponses:

184

L' expression Assert.Throws interceptera l'exception et affirmera le type. Cependant, vous appelez la méthode testée en dehors de l'expression d'assert et échouez ainsi au scénario de test.

[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
    //arrange
    ProfileRepository profiles = new ProfileRepository();
    // act & assert
    Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}

Si vous êtes déterminé à suivre AAA, vous pouvez extraire l'action dans sa propre variable.

[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
    //arrange
    ProfileRepository profiles = new ProfileRepository();
    //act
    Action act = () => profiles.GetSettingsForUserID("");
    //assert
    var exception = Assert.Throws<ArgumentException>(act);
    //The thrown exception can be used for even more detailed assertions.
    Assert.Equal("expected error message here", exception.Message);
}

Notez comment l'exception peut également être utilisée pour les assertions détaillées de mode

Nkosi
la source
5
Si vous utilisez des méthodes asynchrones, Visual Studio émet un avertissement avec la syntaxe ci-dessus. Il préfère ceci:async Task act() => await service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);
Alec
5
En fait, pour moi, cela a abouti à une erreur, `` ne peut pas convertir implicitement Task en Func <Task> '', alors que si je viens de le mettre, Task act() => service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);c'est satisfait et fonctionne bien.
Alec
comment travailler avec async / await a-t-il un impact sur cela? lorsque j'essaie de le faire en utilisant ThrowsAsync dans mon test, il n'atteint jamais la ligne Assert.Equal car il lance avec succès l'erreur et quitte le test. tester l'eau pour voir si cela devrait être une nouvelle question ...
nathanjw
@AlecDenholm Merci! C'est la seule chose qui a fonctionné pour moi. Je pense que certaines des autres suggestions ne fonctionnent pas vraiment correctement pour les trucs asynchrones.
marque déposée
45

Si vous voulez être rigide sur AAA, vous pouvez utiliser Record.Exception de xUnit pour capturer l'exception dans votre étape Act.

Vous pouvez ensuite faire des assertions en fonction de l'exception capturée à l'étape Assert.

Un exemple de ceci peut être vu dans les tests xUnits .

[Fact]
public void Exception()
{
    Action testCode = () => { throw new InvalidOperationException(); };

    var ex = Record.Exception(testCode);

    Assert.NotNull(ex);
    Assert.IsType<InvalidOperationException>(ex);
}

C'est à vous de décider du chemin que vous souhaitez suivre, et les deux chemins sont entièrement pris en charge par ce que propose xUnit.

Bhargav Rao
la source
1
FWIW, Cette solution est excellente si vous avez besoin de valider le message d'exception, etc. Je pense que c'est à ce moment-là que vous pouvez utiliser Record.Exception.
Jeff LaFay
@JeffLaFay J'apprécie que je suis un peu en retard à la fête ici, en quoi cela différerait-il de l'utilisation var exception = Assert.Throws<InvalidOperationException>(testCode);et de l'affirmation exception.Message? ou est-ce juste une autre saveur de réaliser la même chose?
ColinM
3

Vous pourriez envisager quelque chose comme ceci si vous voulez vous en tenir à AAA:

// Act 
Task act() => handler.Handle(request);

// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
Yves Rochon
la source