Como capturar uma exceção no MSTest e não enlouquecer?
\n\nVocê está escrevendo testes unitários em C# e de repente percebe: seu método deve lançar uma exceção em uma determinada situação. Como verificar isso? Se você pesquisou "How do I use Assert to verify that an exception has been thrown with MSTest", não está sozinho — esta é uma das perguntas mais populares no Stack Overflow. E hoje vamos resolvê-la de uma vez por todas.
\n\nNeste artigo, mostrarei a você todas as formas de verificar exceções no MSTest: desde o atributo clássico até o moderno Assert.ThrowsException. Você aprenderá a escrever testes limpos e confiáveis que não falharão no momento mais inoportuno. Vamos lá!
Por que a verificação de exceções é importante?
\n\nImagine: você tem um método Divide(int a, int b). Se b = 0, ele deve lançar DivideByZeroException. Sem um teste, você só descobrirá isso quando a aplicação cair em produção. Um teste unitário com verificação de exceção é o seu cabo de segurança.
No MSTest (framework de teste de unidade da Microsoft), existem várias abordagens. Vamos analisar cada uma, mas começaremos pela mais moderna e recomendada.
\n\nMétodo 1: Assert.ThrowsException (O mais correto)
\n\nA partir do MSTest V2 (versão 2.x), temos o método embutido Assert.ThrowsException<T>. É elegante, legível e flexível. A sintaxe é simples:
[TestMethod]\npublic void Divide_ByZero_ThrowsDivideByZeroException()\n{\n // Arrange\n var calculator = new Calculator();\n\n // Act & Assert\n Assert.ThrowsException<DivideByZeroException>(() => calculator.Divide(10, 0));\n}\n\nO que acontece aqui? Passamos uma expressão lambda com a chamada do método. O MSTest a executa dentro de um bloco try-catch. Se a exceção NÃO for lançada — o teste falha. Se for lançada uma exceção de outro tipo — também falha. Apenas a exceção esperada resulta em sucesso.
\n\nExemplo avançado: verificar a mensagem da exceção
\n\nÀs vezes, é necessário garantir não apenas o tipo da exceção, mas também sua mensagem. Para isso, use a sobrecarga com o parâmetro message ou verifique as propriedades do objeto de exceção:
[TestMethod]\npublic void GetUserById_InvalidId_ThrowsWithCorrectMessage()\n{\n // Arrange\n var service = new UserService();\n\n // Act\n var exception = Assert.ThrowsException<ArgumentException>(() => service.GetUserById(-1));\n\n // Assert adicional\n Assert.AreEqual("Id must be positive", exception.Message);\n}\n\nObserve: Assert.ThrowsException retorna o objeto de exceção! Isso permite verificar qualquer uma de suas propriedades: InnerException, Data, StackTrace (embora este último geralmente não seja verificado).
Método 2: Atributo [ExpectedException] (Obsoleto, mas ainda em uso)
\n\nAntigamente (no MSTest V1), usava-se um atributo sobre o próprio método de teste. Ficava assim:
\n\n[TestMethod]\n[ExpectedException(typeof(DivideByZeroException))]\npublic void Divide_ByZero_ThrowsDivideByZeroException_OldWay()\n{\n var calculator = new Calculator();\n calculator.Divide(10, 0);\n}\n\nParece conveniente? Mas existem três problemas:
\n- \n
- Não é possível verificar a mensagem da exceção. Você não sabe se a exceção correta foi lançada. \n
- Qualquer exceção desse tipo é sucesso. Mesmo que tenha sido lançada de outra linha de código (por exemplo, no Arrange). \n
- Não é possível executar código após a exceção. O método de teste é interrompido imediatamente. \n
Portanto, em novos projetos, use Assert.ThrowsException. O atributo [ExpectedException] está marcado como deprecated (obsoleto) e pode ser removido em versões futuras.
Método 3: Try-catch manual (Para radicais)
\n\nÀs vezes (muito raramente) você pr