Como verifico “nenhuma exceção ocorreu” em meu teste de unidade MSTest?


88

Estou escrevendo um teste de unidade para este método que retorna "void". Eu gostaria de ter um caso em que o teste seja aprovado quando não houver exceção lançada. Como faço para escrever isso em C #?

Assert.IsTrue(????)

(Meu palpite é que devo verificar, mas o que entra em "???")

Espero que minha pergunta esteja clara o suficiente.


Você está usando o MSTest ou o NUnit?
Matt Grande

2
No MSTest, exceções não capturadas farão com que os testes falhem automaticamente. Você está tentando contabilizar exceções detectadas?
Phil

Você pode pesquisar "try-catch para C #" e isso o instruirá sobre como lidar com exceções sendo lançadas ou não lançadas.
Foggzie

1
Se NUnit, olhe para Assert.That (lambda) .Throws.Nothing (embora eu ache que isso mudou recentemente)
Matt Grande

Respostas:


138

Seu teste de unidade irá falhar de qualquer maneira se uma exceção for lançada - você não precisa colocar uma declaração especial.

Este é um dos poucos cenários onde você verá testes de unidade sem nenhuma asserção - o teste irá falhar implicitamente se uma exceção for levantada.

No entanto, se você realmente deseja escrever uma declaração para isso - talvez para ser capaz de capturar a exceção e relatar "não esperava nenhuma exceção, mas conseguiu isso ...", você pode fazer isso:

[Test]
public void TestNoExceptionIsThrownByMethodUnderTest()
{
    var myObject = new MyObject();

    try
    {
        myObject.MethodUnderTest();
    }
    catch (Exception ex)
    {
        Assert.Fail("Expected no exception, but got: " + ex.Message);
    }
}

(o acima é um exemplo para NUnit, mas o mesmo vale para MSTest)


Obviamente, você não deve pegar essas exceções para que isso seja verdade.
Servy

7
Um teste só falhará se uma exceção não detectada for lançada. Dependendo do código nos manipuladores de exceção, os testes de unidade podem passar.
ediblecode

1
É útil para a Sra. Unittest, então não há método Assert.DoesNotThrow (() no Unittest.
Başar Kaya

25

No NUnit, você pode usar:

Assert.DoesNotThrow(<expression>); 

para afirmar que seu código não lança uma exceção. Embora o teste falhe se uma exceção for lançada mesmo que não haja Assert em torno dela, o valor desta abordagem é que você pode distinguir entre expectativas não atendidas e bugs em seus testes, e você tem a opção de adicionar uma mensagem personalizada que será exibido em sua saída de teste. Uma saída de teste bem formulada pode ajudá-lo a localizar erros em seu código que causaram a falha de um teste.

Acho que é válido adicionar testes para garantir que seu código não está lançando exceções; por exemplo, imagine que você está validando a entrada e precisa converter uma string de entrada em uma longa. Pode haver ocasiões em que a string é nula, e isso é aceitável, portanto, você deseja garantir que a conversão da string não lance uma exceção. Haverá, portanto, um código para lidar com essa ocasião e, se você não escreveu um teste para isso, perderá a cobertura de uma importante parte da lógica.


1
O DoesNotThrow explícito é bom. Se você está acostumado a ver Assert. * Em um teste, pode pensar que o outro cara foi preguiçoso e esqueceu.
Matt Beckman

Existe algum equivalente para ele em vstest ou mstest?
Dan Csharpster

1
@DanCsharpster, não acho que haja, pelo menos no MSTest - quando eu precisei dessa funcionalidade no MSTest no passado, fiz algo assim: public class TestBase { //believe me, I don't like this anymore than you do. protected void AssertDoesNotThrow(Action action, string message) { try { action(); } catch (Exception) { Assert.Fail(message); } } }
Clarkeye

@Clarkeye, é uma ideia interessante. Obrigado! Com sorte, eles aprenderão a copiar melhor o NUnit, em versões futuras. Também pensei em escrever um adaptador entre vstest e NUnit.
Dan Csharpster

@DanCsharpster, uma coisa que você pode querer dar uma olhada são as afirmações fluentes, que têm um ótimo suporte para ShouldThrow e ShouldNotThrow: github.com/dennisdoomen/fluentassertions/wiki#exceptions . Os documentos dizem que ele é compatível com MSTest (embora eu só o tenha usado com XUnit e NUnit). Pode não fazer tudo o que você deseja, mas você pode misturá-lo com as afirmações do MSTest de qualquer maneira.
Clarkeye

11

Não teste se algo não acontece . É como garantir que o código não seja quebrado . Isso está meio que implícito, todos nós nos esforçamos por um código sem falhas e sem falhas. Você quer escrever testes para isso? Por que apenas um método? Você não quer que todos os seus métodos sejam testados para que não gerem exceções ? Seguindo esse caminho, você acabará com um teste extra, fictício e sem assertividade para cada método em sua base de código. Não traz nenhum valor.

Claro, se a sua exigência é para verificar método faz exceções captura , você faz teste que (ou revertê-la um pouco, teste que não jogue o que é suposto para pegar).

No entanto, a abordagem / práticas gerais permanecem intactas - você não escreve testes para alguns requisitos artificiais / vagos que estão fora do escopo do código testado (e testar se "funciona" ou "não joga" é geralmente um exemplo de (especialmente em cenários em que as responsabilidades do método são bem conhecidas).

Para simplificar - concentre-se no que seu código deve fazer e teste para isso.


10
-1 Posso pensar em uma funcionalidade positiva que requer e exceção não seja lançada. Para um - método cujo trabalho é lidar com exceções, registre-as e execute uma ação - sem lançar a exceção ainda mais. Você fez um bom argumento geral - mas então fale em termos absolutos como se fosse sempre verdade.
Rob Levine

3
@RobLevine: Eu entendo seu exemplo e sei que você escreve testes nesses casos. No entanto, como você notou, meu ponto de fato era sobre a prática mais geral - por assim dizer, testar o que seu código deve fazer versus testar o que seu código não faz. Refiz minha postagem um pouco, para que meu ponto fique mais claro e mais próximo do que eu tinha em mente. Também lhe dá a oportunidade de reconsiderar seu voto. Obrigado pelo esclarecimento e desculpe pelo atraso na resposta.
km

4
voto negativo removido - não ficarei tão feliz com o voto negativo da próxima vez!
Rob Levine

4
Em nosso projeto, temos a classe htmlvalidator, que lança exceções se o html não for válido. Por exemplo, quando a entrada do usuário (usando o console) javascript em rich combo. Portanto, no meu código de caso, o que meu código faz é não lançar exceção (abordagem de lista branca) e preciso testar isso.
Machet

1
Discordo desta resposta. Testar a ausência de algo às vezes em um determinado cenário pode ser um teste válido.
bytedev

7

Esta classe de auxiliar me arranhou com o MSTest. Talvez possa arranhar o seu também.

[TestMethod]
public void ScheduleItsIneligibilityJob_HasValid_CronSchedule()
{
    // Arrange
    var factory = new StdSchedulerFactory();
    IScheduler scheduler = factory.GetScheduler();

    // Assert
    AssertEx.NoExceptionThrown<FormatException>(() =>
        // Act
        _service.ScheduleJob(scheduler)
    );
}

public sealed class AssertEx
{
    public static void NoExceptionThrown<T>(Action a) where T:Exception
    {
        try
        {
            a();
        }
        catch (T)
        {
            Assert.Fail("Expected no {0} to be thrown", typeof(T).Name);
        }
    }
}

@Remco Beurskens - adicionar uma captura geral {} no final de NoExceptionThrown <T> suprimirá outros erros, o que não é uma consequência pretendida do método. Este não é um método de propósito geral para suprimir todas as exceções. A intenção é que ele falhe apenas quando uma exceção do tipo conhecido É lançada.
JJS

1
Isso é muito antigo agora, mas Asserttem um acessador de propriedade singleton, Thatque pode ser usado como gancho para métodos de extensão. Pode ser mais puro e mais detectável, para ter Assert.That.DoesNotThrow()em vez de AssertEx.DoesNotThrow(). Esta é apenas uma opinião.
Richard Hauer

3

Gosto de ver um Assert.Whateverno final de cada teste, apenas para consistência ... sem um, posso realmente ter certeza de que não deveria haver um?

Para mim, isso é tão simples quanto colocar Assert.IsTrue(true);

Eu sei que eu não acidentalmente colocar esse código lá, e, portanto, eu deveria estar confiante o suficiente em um skim rápida através de que este era como pretendido.

    [TestMethod]
    public void ProjectRejectsGappedVersioningByDefault() {

        var files = new List<ScriptFile>();
        files.Add(ScriptProjectTestMocks.GetVersion1to2());
        files.Add(ScriptProjectTestMocks.GetVersion3to4());

        Assert.Throws<ScriptProject.InvalidProjectFormatException>(() => {
            var sut = new ScriptProject(files);
        });

    }

    [TestMethod]
    public void ProjectAcceptsGappedVersionsExplicitly() {

        var files = new List<ScriptFile>();
        files.Add(ScriptProjectTestMocks.GetVersion1to2());
        files.Add(ScriptProjectTestMocks.GetVersion3to4());

        var sut = new ScriptProject(files, true);

        Assert.IsTrue(true);   // Assert.Pass() would be nicer... build it in if you like

    }

Não é o mesmo. Se seu código lançar, nenhuma asserção será atingida e sua execução de teste falhará. Você deseja se conectar à estrutura de teste declarando uma condição.
DvS

1

Meu amigo Tim me contou sobre ExpectedException . Eu realmente gosto deste b / c é mais sucinto, menos código e muito explícito que você está testando uma exceção.

[TestMethod()]
[ExpectedException(typeof(System.Exception))]
public void DivideTest()
{
    int numerator = 4;
    int denominator = 0;
    int actual = numerator / denominator;
}

Você pode ler muito mais sobre isso aqui: ExpectedException Attribute Usage .


1
o OP está pedindo uma exceção.
Daniel A. White

Vou deixar essa resposta aqui. Eu encontrei essa pergunta enquanto pesquisava no google como testar as exceções e acho que essa resposta precisa estar aqui. O OP teve sua pergunta respondida há 7 anos. Acho que até mesmo o link para a outra resposta é útil.
Jess

Bom velho Tim. 🤔
ruffin
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.