[TestMethod][ExpectedException(typeof(ArgumentException),"A userId of null was inappropriately allowed.")]publicvoidNullUserIdInConstructor(){LogonInfo logonInfo =newLogonInfo(null,"P@ss0word");}
O atributo ExpectedException acima também funciona no NUnit (mas [TestMethod] deve ser [Test]).
Dbkk 01/06/09
5
@dbkk: não funciona exatamente o mesmo em NUnit - a mensagem é tratada como uma cadeia que precisa matcvh a mensagem de exceção (e IU acho que faz mais sentido)
Ruben Bartelink
29
Esse atributo faz o trabalho e é um recurso interno para programadores de c #, mas não recomendo usá-lo, pois não é flexível o suficiente. Considere o que acontece se o tipo de exceção for lançado pelo seu código de configuração de teste: o teste passa, mas não fez o que você esperava. Ou, se você quiser testar o estado do objeto de exceção. Normalmente, quero usar StringAssert.Contains (e.Message ...) em vez de testar a mensagem inteira. Use um método de afirmação, conforme descrito em outras respostas.
steve
3
Evite usar ExpectedException no NUnit, pois ele será descartado no NUnit 3.0. Eu prefiro usar Assert.Throws <SpecificException> ()
Terence
5
Você pode usar Assert.ThrowsException <T> e Assert.ThrowsExceptionAsync <T> dentro de MsTest.
Gopal Krishnan
257
Normalmente, sua estrutura de teste terá uma resposta para isso. Mas se não for flexível o suficiente, você sempre poderá fazer isso:
try{
somethingThatShouldThrowAnException();Assert.Fail();// If it gets to this line, no exception was thrown}catch(GoodException){}
Como @Jonas aponta, isso NÃO funciona para capturar uma exceção de base:
try{
somethingThatShouldThrowAnException();Assert.Fail();// raises AssertionException}catch(Exception){// Catches the assertion exception, and the test passes}
Se você absolutamente precisar capturar Exception, será necessário repetir novamente o Assert.Fail (). Mas, realmente, este é um sinal de que você não deveria escrever isso à mão; verifique se há opções na estrutura de teste ou veja se você pode lançar uma exceção mais significativa para testar.
catch(AssertionException){throw;}
Você poderá adaptar essa abordagem ao que quiser - incluindo a especificação de quais tipos de exceções serão capturadas. Se você espera apenas determinados tipos, finalize os catchblocos com:
}catch(GoodException){}catch(Exception){// not the right kind of exceptionAssert.Fail();}
+1, uso dessa maneira em vez do atributo quando preciso fazer afirmações além do tipo de exceção. Por exemplo, e se for necessário verificar se determinados campos na instância de exceção estão definidos com certos valores.
Pavel Repin
2
Você não precisa especificar a mensagem de erro. Isso é suficiente: [ExpectedException (typeof (ArgumentException))]]
mibollma
5
Eu acho que essa solução é a melhor. [ExpectedException (typeof (ArgumentException))] tem seus usos, se o teste for simples, mas, no meu ponto de vista, é uma solução preguiçosa e estar confortável pode levar a armadilhas. Essa solução fornece controle específico para você fazer um teste mais correto, além de poder fazer um Writeline de teste no relatório de execução de teste, de que a exceção foi realmente lançada conforme o esperado.
evilfish
12
Tenha cuidado com isso, porque Assert.Fail () gera uma exceção, se você a captura, o teste passa!
Jonas
4
@ Vinnyq12 O que quero dizer é que o primeiro teste no exemplo acima nunca falhará. Um teste falhar se uma exceção é lançada (e não "pegar" pela ExpectedExceptionAttribute)
Jonas
113
Meu método preferido para implementar isso é escrever um método chamado Throws e usá-lo como qualquer outro método Assert. Infelizmente, o .NET não permite que você escreva um método de extensão estática; portanto, você não pode usá-lo como se realmente pertencesse à compilação na classe Assert; basta criar outro chamado MyAssert ou algo semelhante. A classe fica assim:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace YourProject.Tests{publicstaticclassMyAssert{publicstaticvoidThrows<T>(Action func )where T :Exception{var exceptionThrown =false;try{
func.Invoke();}catch( T ){
exceptionThrown =true;}if(!exceptionThrown ){thrownewAssertFailedException(String.Format("An exception of type {0} was expected, but not thrown",typeof(T)));}}}}
Isso significa que seu teste de unidade se parece com isso:
Livre-se do sinalizador bool e coloque o arremesso em linha diretamente após a chamada para uma implementação mais compacta.
gt
11
A única coisa que melhora isso é fazer com que a função retorne a exceção capturada, para que você possa continuar afirmando que coisas como os atributos na exceção estão corretas.
Mark Hildreth
2
Obrigado! Essa parece ser a melhor abordagem para mim, porque é uma maneira curta de testar várias exceções em um método. Também é muito mais legível.
David Sherret
2
Os atributos @MickeyPerlstein quebram as regras AAA para teste. Especificamente, se o seu Organizar lançar a exceção antes mesmo de você chegar à lei, seu teste será aprovado ... eek!
Isso funciona, mas eu não recomendo isso em geral, pois a lógica é excessivamente complicada. Não estou dizendo que é complicado, mas considere se você escrever esse bloco de código para vários testes - 10s, 100s de testes. Essa lógica precisa ser cultivada em um método de afirmação bem projetado. Veja outras respostas.
steve
35
Desconfie de usar ExpectedException, pois isso pode levar a várias armadilhas, conforme demonstrado aqui:
Se você precisar testar exceções, haverá menos maneiras desaprovadas. Você pode usar o método try {act / fail} catch {assert}, que pode ser útil para estruturas que não têm suporte direto para testes de exceção diferentes de ExpectedException.
Uma alternativa melhor é usar o xUnit.NET, que é uma estrutura de teste de unidade muito moderna, voltada para o futuro e extensível, que aprendeu com todos os outros erros e melhorou. Um desses aprimoramentos é o Assert.Throws, que fornece uma sintaxe muito melhor para a declaração de exceções.
A maneira como os testes de unidade param para informar você sobre a exceção ao usar ExpectedException me deixa louco. Por que a MS achou uma boa idéia ter uma etapa manual nos testes automatizados? Obrigado pelos links.
Ant
@ Ant: A MS copiou o NUnit ... então a verdadeira questão é: por que o NUnit achava que era uma boa idéia?
jrista
28
O MSTest (v2) agora tem uma função Assert.ThrowsException que pode ser usada assim:
Assert.ThrowsException<System.FormatException>(()=>{Story actual =PersonalSite.Services.Content.ExtractHeader(String.Empty);});
Você pode instalá-lo com nuget: Install-Package MSTest.TestFramework
publicstaticTExceptionAssertThrows<TException>(Action action)whereTException:Exception{try{
action();}catch(TException ex){return ex;}Assert.Fail("Expected exception was not thrown");returnnull;}
Você pode baixar um pacote do Nuget usando: PM> Install-Package MSTestExtensions que adiciona a sintaxe Assert.Throws () no estilo de nUnit / xUnit ao MsTest.
Instruções de alto nível: baixe o assembly e herde do BaseTest e você pode usar a sintaxe Assert.Throws () .
O método principal para a implementação Throws é o seguinte:
publicstaticvoidThrows<T>(Action task,string expectedMessage,ExceptionMessageCompareOptions options)where T :Exception{try{
task();}catch(Exception ex){AssertExceptionType<T>(ex);AssertExceptionMessage(ex, expectedMessage, options);return;}if(typeof(T).Equals(newException().GetType())){Assert.Fail("Expected exception but no exception was thrown.");}else{Assert.Fail(string.Format("Expected exception of type {0} but no exception was thrown.",typeof(T)));}}
Existem muitas outras respostas. Para mim, eu estava procurando uma maneira abreviada de testar condições de falha conhecidas apenas pelo Tipo de Exceção, o que facilita os casos de teste legíveis mais fáceis. NOTA: o tipo de exceção não corresponde às classes de exceções herdadas, como um try-catch padrão; portanto, o exemplo acima não interceptará um, ArgumentExceptionpor exemplo. A antiga tentativa de captura e teste da resposta de exceção ainda é preferida se você tiver critérios avançados para testar, mas, para muitos dos meus casos, isso ajuda muito!
21419 Chris Schaller
5
Não recomendo usar o atributo ExpectedException (já que é muito restritivo e propenso a erros) ou escrever um bloco try / catch em cada teste (já que é muito complicado e propenso a erros). Use um método de afirmação bem projetado - fornecido pela sua estrutura de teste ou escreva seu próprio. Aqui está o que eu escrevi e uso.
publicstaticclassExceptionAssert{privatestatic T GetException<T>(Action action,string message="")where T :Exception{try{
action();}catch(T exception){return exception;}thrownewAssertFailedException("Expected exception "+typeof(T).FullName+", but none was propagated. "+ message);}publicstaticvoidPropagates<T>(Action action)where T :Exception{Propagates<T>(action,"");}publicstaticvoidPropagates<T>(Action action,string message)where T :Exception{GetException<T>(action, message);}publicstaticvoidPropagates<T>(Action action,Action<T> validation)where T :Exception{Propagates(action, validation,"");}publicstaticvoidPropagates<T>(Action action,Action<T> validation,string message)where T :Exception{
validation(GetException<T>(action, message));}}
Exemplo usa:
[TestMethod]publicvoidRun_PropagatesWin32Exception_ForInvalidExeFile(){(test setup that might propagate Win32Exception)ExceptionAssert.Propagates<Win32Exception>(()=>CommandExecutionUtil.Run(Assembly.GetExecutingAssembly().Location,newstring[0]));(more asserts or something)}[TestMethod]publicvoidRun_PropagatesFileNotFoundException_ForExecutableNotFound(){(test setup that might propagate FileNotFoundException)ExceptionAssert.Propagates<FileNotFoundException>(()=>CommandExecutionUtil.Run("NotThere.exe",newstring[0]),
e =>StringAssert.Contains(e.Message,"NotThere.exe"));(more asserts or something)}
NOTAS
Retornar a exceção em vez de oferecer suporte a um retorno de chamada de validação é uma idéia razoável, exceto que isso torna a sintaxe de chamada dessa declaração muito diferente das outras que eu uso.
Diferentemente de outros, eu uso 'propaga' em vez de 'lança', pois só podemos testar se uma exceção se propaga a partir de uma chamada. Não podemos testar diretamente se uma exceção é lançada. Mas suponho que você possa imaginar lances como significados: jogados e não capturados.
PENSAMENTO FINAL
Antes de mudar para esse tipo de abordagem, considerei usar o atributo ExpectedException quando um teste verificou apenas o tipo de exceção e usar um bloco try / catch se mais validação fosse necessária. Mas não apenas precisaria pensar em qual técnica usar para cada teste, mas alterar o código de uma técnica para outra conforme as necessidades mudassem não era um esforço trivial. Usar uma abordagem consistente economiza esforço mental.
Então, em resumo, essa abordagem é esportiva: facilidade de uso, flexibilidade e robustez (difícil de fazer errado).
O auxiliar fornecido pelo @Richiban acima funciona muito bem, exceto que não lida com a situação em que uma exceção é lançada, mas não com o tipo esperado. Os seguintes endereços que:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace YourProject.Tests{publicstaticclassMyAssert{/// <summary>/// Helper for Asserting that a function throws an exception of a particular type./// </summary>publicstaticvoidThrows<T>(Action func )where T :Exception{Exception exceptionOther =null;var exceptionThrown =false;try{
func.Invoke();}catch( T ){
exceptionThrown =true;}catch(Exception e){
exceptionOther = e;}if(!exceptionThrown ){if(exceptionOther !=null){thrownewAssertFailedException(String.Format("An exception of type {0} was expected, but not thrown. Instead, an exception of type {1} was thrown.",typeof(T), exceptionOther.GetType()),
exceptionOther
);}thrownewAssertFailedException(String.Format("An exception of type {0} was expected, but no exception was thrown.",typeof(T)));}}}}
Hmmm ... eu entendo a idéia, mas não tenho certeza se concordo que é melhor. Só porque queremos garantir que uma exceção específica seja levantada não significa que todas as outras devem ser agrupadas como uma falha de afirmação. IMHO uma exceção desconhecida deve apenas aumentar a pilha como faria em qualquer outra operação de afirmação.
Crono
@ Martin eu remover o código envolvendo exceptionOther e simplesmente relançar a partir da segunda cláusula catch
Tom Lint
4
Como você menciona o uso de outras classes de teste, uma opção melhor que o ExpectedExceptionatributo é usar o Should.Throw de Shoudly .
Digamos que tenhamos um requisito de que o cliente precise de um endereço para criar um pedido . Caso contrário, o CreateOrderForCustomermétodo deve resultar em um ArgumentException. Então poderíamos escrever:
[TestMethod]publicvoidNullUserIdInConstructor(){var customer =newCustomer(name :="Justin", address :=null};Should.Throw<ArgumentException>(()=>{var order =CreateOrderForCustomer(customer)});}
Isso é melhor do que usar um ExpectedException atributo, porque estamos sendo específicos sobre o que deve gerar o erro. Isso torna os requisitos de nossos testes mais claros e também facilita o diagnóstico quando o teste falha.
Observe que também existe um Should.ThrowAsyncteste de método assíncrono.
Nos testes de unidade integrados do VS, se você simplesmente deseja verificar se "qualquer exceção" é lançada, mas não sabe o tipo, pode usar um catch all:
Bem, vou resumir o que todo mundo aqui disse antes ... De qualquer forma, aqui está o código que construí de acordo com as boas respostas :) Tudo o que resta fazer é copiar e usar ...
/// <summary>/// Checks to make sure that the input delegate throws a exception of type TException./// </summary>/// <typeparam name="TException">The type of exception expected.</typeparam>/// <param name="methodToExecute">The method to execute to generate the exception.</param>publicstaticvoidAssertRaises<TException>(Action methodToExecute)whereTException:System.Exception{try{
methodToExecute();}catch(TException){return;}catch(System.Exception ex){Assert.Fail("Expected exception of type "+typeof(TException)+" but type of "+ ex.GetType()+" was thrown instead.");}Assert.Fail("Expected exception of type "+typeof(TException)+" but no exception was thrown.");}
Isso vai depender de qual estrutura de teste você está usando?
No MbUnit, por exemplo, você pode especificar a exceção esperada com um atributo para garantir que você esteja recebendo a exceção que realmente espera.
Embora essa seja uma pergunta antiga, eu gostaria de acrescentar um novo pensamento à discussão. Eu estendi o padrão Organizar, Agir, Assertar para ser Esperado, Organizar, Agir, Assertar. Você pode criar um ponteiro de exceção esperado e afirmar que ele foi atribuído. Isso parece mais limpo do que fazer seus Asserts em um bloco catch, deixando a seção Act principalmente apenas para uma linha de código chamar o método em teste. Você também não precisa de Assert.Fail();ou returnpara vários pontos no código. Qualquer outra exceção lançada fará com que o teste falhe, porque não será capturado e, se uma exceção do seu tipo esperado for lançada, mas não for a que você estava esperando, afirmando contra a mensagem ou outras propriedades de a exceção ajuda a garantir que seu teste não seja aprovado inadvertidamente.
Respostas:
Para "Teste da equipe do Visual Studio", parece que você aplica o atributo ExpectedException ao método do teste.
Exemplo da documentação aqui: Um passo a passo de teste de unidade com o Visual Studio Team Test
fonte
Normalmente, sua estrutura de teste terá uma resposta para isso. Mas se não for flexível o suficiente, você sempre poderá fazer isso:
Como @Jonas aponta, isso NÃO funciona para capturar uma exceção de base:
Se você absolutamente precisar capturar Exception, será necessário repetir novamente o Assert.Fail (). Mas, realmente, este é um sinal de que você não deveria escrever isso à mão; verifique se há opções na estrutura de teste ou veja se você pode lançar uma exceção mais significativa para testar.
Você poderá adaptar essa abordagem ao que quiser - incluindo a especificação de quais tipos de exceções serão capturadas. Se você espera apenas determinados tipos, finalize os
catch
blocos com:fonte
Meu método preferido para implementar isso é escrever um método chamado Throws e usá-lo como qualquer outro método Assert. Infelizmente, o .NET não permite que você escreva um método de extensão estática; portanto, você não pode usá-lo como se realmente pertencesse à compilação na classe Assert; basta criar outro chamado MyAssert ou algo semelhante. A classe fica assim:
Isso significa que seu teste de unidade se parece com isso:
Que se parece e se comporta muito mais com o resto das suas sintaxes de teste de unidade.
fonte
Assert.ThrowsException<T>
eAssert.ThrowsExceptionAsync<T>
- consulte blogs.msdn.microsoft.com/visualstudioalm/2017/02/25/…se você usa NUNIT, pode fazer algo assim:
Também é possível armazenar a exceção lançada para validá-la ainda mais:
Consulte: http://nunit.org/docs/2.5/exceptionAsserts.html
fonte
Se você estiver usando o MSTest, que originalmente não tinha um
ExpectedException
atributo, você pode fazer o seguinte:fonte
Desconfie de usar ExpectedException, pois isso pode levar a várias armadilhas, conforme demonstrado aqui:
http://geekswithblogs.net/sdorman/archive/2009/01/17/unit-testing-and-expected-exceptions.aspx
E aqui:
http://xunit.github.io/docs/comparisons.html
Se você precisar testar exceções, haverá menos maneiras desaprovadas. Você pode usar o método try {act / fail} catch {assert}, que pode ser útil para estruturas que não têm suporte direto para testes de exceção diferentes de ExpectedException.
Uma alternativa melhor é usar o xUnit.NET, que é uma estrutura de teste de unidade muito moderna, voltada para o futuro e extensível, que aprendeu com todos os outros erros e melhorou. Um desses aprimoramentos é o Assert.Throws, que fornece uma sintaxe muito melhor para a declaração de exceções.
Você pode encontrar o xUnit.NET no github: http://xunit.github.io/
fonte
O MSTest (v2) agora tem uma função Assert.ThrowsException que pode ser usada assim:
Você pode instalá-lo com nuget:
Install-Package MSTest.TestFramework
fonte
Em um projeto em que estou trabalhando, temos outra solução para isso.
Primeiro, não gosto do ExpectedExceptionAttribute, pois leva em consideração qual chamada de método causou a exceção.
Eu faço isso com um método auxiliar em seu lugar.
Teste
HelperMethod
Legal, não é;)
fonte
É um atributo no método de teste ... você não usa Assert. Se parece com isso:
fonte
Você pode baixar um pacote do Nuget usando: PM> Install-Package MSTestExtensions que adiciona a sintaxe Assert.Throws () no estilo de nUnit / xUnit ao MsTest.
Instruções de alto nível: baixe o assembly e herde do BaseTest e você pode usar a sintaxe Assert.Throws () .
O método principal para a implementação Throws é o seguinte:
Divulgação: Eu montei este pacote.
Mais informações: http://www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html
fonte
Você pode conseguir isso com uma linha simples.
Se sua operação
foo.bar()
é assíncrona:Se
foo.bar()
não for assíncronofonte
ArgumentException
por exemplo. A antiga tentativa de captura e teste da resposta de exceção ainda é preferida se você tiver critérios avançados para testar, mas, para muitos dos meus casos, isso ajuda muito!Não recomendo usar o atributo ExpectedException (já que é muito restritivo e propenso a erros) ou escrever um bloco try / catch em cada teste (já que é muito complicado e propenso a erros). Use um método de afirmação bem projetado - fornecido pela sua estrutura de teste ou escreva seu próprio. Aqui está o que eu escrevi e uso.
Exemplo usa:
NOTAS
Retornar a exceção em vez de oferecer suporte a um retorno de chamada de validação é uma idéia razoável, exceto que isso torna a sintaxe de chamada dessa declaração muito diferente das outras que eu uso.
Diferentemente de outros, eu uso 'propaga' em vez de 'lança', pois só podemos testar se uma exceção se propaga a partir de uma chamada. Não podemos testar diretamente se uma exceção é lançada. Mas suponho que você possa imaginar lances como significados: jogados e não capturados.
PENSAMENTO FINAL
Antes de mudar para esse tipo de abordagem, considerei usar o atributo ExpectedException quando um teste verificou apenas o tipo de exceção e usar um bloco try / catch se mais validação fosse necessária. Mas não apenas precisaria pensar em qual técnica usar para cada teste, mas alterar o código de uma técnica para outra conforme as necessidades mudassem não era um esforço trivial. Usar uma abordagem consistente economiza esforço mental.
Então, em resumo, essa abordagem é esportiva: facilidade de uso, flexibilidade e robustez (difícil de fazer errado).
fonte
O auxiliar fornecido pelo @Richiban acima funciona muito bem, exceto que não lida com a situação em que uma exceção é lançada, mas não com o tipo esperado. Os seguintes endereços que:
fonte
Como você menciona o uso de outras classes de teste, uma opção melhor que o
ExpectedException
atributo é usar o Should.Throw de Shoudly .Digamos que tenhamos um requisito de que o cliente precise de um endereço para criar um pedido . Caso contrário, o
CreateOrderForCustomer
método deve resultar em umArgumentException
. Então poderíamos escrever:Isso é melhor do que usar um
ExpectedException
atributo, porque estamos sendo específicos sobre o que deve gerar o erro. Isso torna os requisitos de nossos testes mais claros e também facilita o diagnóstico quando o teste falha.Observe que também existe um
Should.ThrowAsync
teste de método assíncrono.fonte
Como alternativa, você pode tentar testar as exceções, de fato, são lançadas com as próximas 2 linhas em seu teste.
fonte
Nos testes de unidade integrados do VS, se você simplesmente deseja verificar se "qualquer exceção" é lançada, mas não sabe o tipo, pode usar um catch all:
fonte
Bem, vou resumir o que todo mundo aqui disse antes ... De qualquer forma, aqui está o código que construí de acordo com as boas respostas :) Tudo o que resta fazer é copiar e usar ...
fonte
Confira o nUnit Docs para exemplos sobre:
fonte
Isso vai depender de qual estrutura de teste você está usando?
No MbUnit, por exemplo, você pode especificar a exceção esperada com um atributo para garantir que você esteja recebendo a exceção que realmente espera.
fonte
No caso de usar o NUnit , tente o seguinte:
fonte
Existe uma biblioteca incrível chamada NFluent que acelera e facilita a maneira como você escreve suas afirmações .
É bem simples escrever uma afirmação para lançar uma exceção:
fonte
Embora essa seja uma pergunta antiga, eu gostaria de acrescentar um novo pensamento à discussão. Eu estendi o padrão Organizar, Agir, Assertar para ser Esperado, Organizar, Agir, Assertar. Você pode criar um ponteiro de exceção esperado e afirmar que ele foi atribuído. Isso parece mais limpo do que fazer seus Asserts em um bloco catch, deixando a seção Act principalmente apenas para uma linha de código chamar o método em teste. Você também não precisa de
Assert.Fail();
oureturn
para vários pontos no código. Qualquer outra exceção lançada fará com que o teste falhe, porque não será capturado e, se uma exceção do seu tipo esperado for lançada, mas não for a que você estava esperando, afirmando contra a mensagem ou outras propriedades de a exceção ajuda a garantir que seu teste não seja aprovado inadvertidamente.fonte