使用MSTest如何验证来自测试方法的确切错误消息?我知道[ExpectedException(typeof(ApplicationException), error msg)]
没有比较来自我的测试方法的错误消息,不过它正在进行的其他单元测试框架中。
解决这个问题的一种方法是使用一些try catch块编写单元测试,但我需要再写4行。
是否有任何最明智的方法来检查错误消息。
干杯, Pritam
答案 0 :(得分:35)
您可以create your own ExpectedException attribute在Assert
处Exception
发送namespace TestProject
{
public sealed class MyExpectedException : ExpectedExceptionBaseAttribute
{
private Type _expectedExceptionType;
private string _expectedExceptionMessage;
public MyExpectedException(Type expectedExceptionType)
{
_expectedExceptionType = expectedExceptionType;
_expectedExceptionMessage = string.Empty;
}
public MyExpectedException(Type expectedExceptionType, string expectedExceptionMessage)
{
_expectedExceptionType = expectedExceptionType;
_expectedExceptionMessage = expectedExceptionMessage;
}
protected override void Verify(Exception exception)
{
Assert.IsNotNull(exception);
Assert.IsInstanceOfType(exception, _expectedExceptionType, "Wrong type of exception was thrown.");
if(!_expectedExceptionMessage.Length.Equals(0))
{
Assert.AreEqual(_expectedExceptionMessage, exception.Message, "Wrong exception message was returned.");
}
}
}
}
的消息。
<强>代码强>
[TestMethod]
[MyExpectedException(typeof(Exception), "Error")]
public void TestMethod()
{
throw new Exception("Error");
}
<强>用法强>
{{1}}
答案 1 :(得分:15)
使用这个小助手类:
public static class ExceptionAssert
{
public static void Throws<TException>(Action action, string message)
where TException : Exception
{
try
{
action();
Assert.Fail("Exception of type {0} expected; got none exception", typeof(TException).Name);
}
catch (TException ex)
{
Assert.AreEqual(message, ex.Message);
}
catch (Exception ex)
{
Assert.Fail("Exception of type {0} expected; got exception of type {1}", typeof(TException).Name, ex.GetType().Name);
}
}
}
用法:
Foo foo = new Foo();
foo.Property = 42;
ExceptionAssert.Throws<InvalidOperationException>(() => foo.DoSomethingCritical(), "You cannot do anything when Property is 42.");
显式捕获异常的优点是,当另一个成员(例如在初始化期间)抛出异常时,测试不会成功。
答案 2 :(得分:5)
Fluent Assertions(NuGet)具有非常“语言自然”的语法,用于定义单元测试中的期望:
objectundertest.Invoking(o => o.MethodUnderTest()).ShouldThrow<ExpectedException>()
.WithMessage("the expected error message");
使用任何算法(Where(e => ...
)检查错误消息以及检查内部异常及其消息有多种变体。
答案 3 :(得分:5)
我正在寻找一种方法来检查mstest的内部异常的存在和类型,我发现了这个问题。我知道它是一个2岁的主题,但由于我的解决方案不在这里,让我分享一下。
对我来说,解决问题最优雅的方法是创建一个派生属性,这是我的(对不起,但评论和字符串是法语,我的自然语言,但应该是显而易见的):
#region Références
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Text.RegularExpressions;
#endregion
namespace MsTestEx
{
/// <summary>
/// Extention de l'attribut ExpectedException permettant de vérifier plus d'éléments (Message, InnerException, ...)
/// </summary>
public class ExpectedExceptionEx
: ExpectedExceptionBaseAttribute
{
#region Variables locales
private Type _ExpectedException = null;
private string _ExpectedMessage = null;
private Type _ExpectedInnerException = null;
private string _ExpectedInnerExceptionMessage = null;
private bool _IsExpectedMessageRegex = false;
private bool _IsExpectedInnerMessageRegex = false;
private bool _AllowDerivedType = false;
private bool _AllowInnerExceptionDerivedType = false;
private bool _CheckExpectedMessage = false;
private bool _CheckInnerExceptionType = false;
private bool _CheckInnerExceptionMessage = false;
#endregion
#region Propriétés
/// <summary>
/// Vérifie que le message de l'exception correspond à celui-ci.
/// </summary>
public string ExpectedMessage
{
get { return _ExpectedMessage; }
set { _ExpectedMessage = value; _CheckExpectedMessage = true; }
}
/// <summary>
/// Vérifie que le message de l'inner-exception correspond à celui-ci.
/// </summary>
public string ExpectedInnerExceptionMessage
{
get { return _ExpectedInnerExceptionMessage; }
set { _ExpectedInnerExceptionMessage = value; _CheckInnerExceptionMessage = true; }
}
/// <summary>
/// Vérifie que l'exception possède bien une inner-exception du type spécifié.
/// Spécifier "null" pour vérifier l'absence d'inner-exception.
/// </summary>
public Type ExpectedInnerException
{
get { return _ExpectedInnerException; }
set { _ExpectedInnerException = value; _CheckInnerExceptionType = true; }
}
/// <summary>
/// Indique si le message attendu est exprimé via une expression rationnelle.
/// </summary>
public bool IsExpectedMessageRegex
{
get { return _IsExpectedMessageRegex; }
set { _IsExpectedMessageRegex = value; }
}
/// <summary>
/// Indique si le message attendu de l'inner-exception est exprimé via une expression rationnelle.
/// </summary>
public bool IsExpectedInnerMessageRegex
{
get { return _IsExpectedInnerMessageRegex; }
set { _IsExpectedInnerMessageRegex = value; }
}
/// <summary>
/// Indique si les exceptions dérivées sont acceptées.
/// </summary>
public bool AllowDerivedType
{
get { return _AllowDerivedType; }
set { _AllowDerivedType = value; }
}
/// <summary>
/// Indique si les inner-exceptions dérivées sont acceptées.
/// </summary>
public bool AllowInnerExceptionDerivedType
{
get { return _AllowInnerExceptionDerivedType; }
set { _AllowInnerExceptionDerivedType = value; }
}
#endregion
#region Constructeurs
/// <summary>
/// Indique le type d'exception attendu par le test.
/// </summary>
/// <param name="expectedException">Type de l'exception attendu.</param>
public ExpectedExceptionEx(Type expectedException)
{
_ExpectedException = expectedException;
}
#endregion
#region Méthodes
/// <summary>
/// Effectue la vérification.
/// </summary>
/// <param name="exception">Exception levée.</param>
protected override void Verify(Exception exception)
{
Assert.IsNotNull(exception); // Pas eu d'exception, ce n'est pas normal
// Vérification du type de l'exception
Type actualType = exception.GetType();
if (_AllowDerivedType) Assert.IsTrue(_ExpectedException.IsAssignableFrom(actualType), "L'exception reçue n'est pas du type spécifié ni d'un type dérivé.");
else Assert.AreEqual(_ExpectedException, actualType, "L'exception reçue n'est pas du type spécifié.");
// Vérification du message de l'exception
if (_CheckExpectedMessage)
{
if (_IsExpectedMessageRegex)
Assert.IsTrue(Regex.IsMatch(exception.Message, _ExpectedMessage), "Le message de l'exception ne correspond pas à l'expression rationnelle");
else
{
string s1, s2;
if (exception.Message.Length > _ExpectedMessage.Length)
{
s1 = exception.Message;
s2 = _ExpectedMessage;
}
else
{
s1 = _ExpectedMessage;
s2 = exception.Message;
}
Assert.IsTrue(s1.Contains(s2), "Le message de l'exception ne contient pas et n'est pas contenu par le message attendu.");
}
}
if (_CheckInnerExceptionType)
{
if (_ExpectedInnerException == null) Assert.IsNotNull(exception.InnerException);
else
{
// Vérification du type de l'exception
actualType = exception.InnerException.GetType();
if (_AllowInnerExceptionDerivedType) Assert.IsTrue(_ExpectedInnerException.IsAssignableFrom(actualType), "L'inner-exception reçue n'est pas du type spécifié ni d'un type dérivé.");
else Assert.AreEqual(_ExpectedInnerException, actualType, "L'inner-exception reçue n'est pas du type spécifié.");
}
}
if (_CheckInnerExceptionMessage)
{
Assert.IsNotNull(exception.InnerException);
if (_IsExpectedInnerMessageRegex)
Assert.IsTrue(Regex.IsMatch(exception.InnerException.Message, _ExpectedInnerExceptionMessage), "Le message de l'exception ne correspond pas à l'expression rationnelle");
else
{
string s1, s2;
if (exception.InnerException.Message.Length > _ExpectedInnerExceptionMessage.Length)
{
s1 = exception.InnerException.Message;
s2 = _ExpectedInnerExceptionMessage;
}
else
{
s1 = _ExpectedInnerExceptionMessage;
s2 = exception.InnerException.Message;
}
Assert.IsTrue(s1.Contains(s2), "Le message de l'inner-exception ne contient pas et n'est pas contenu par le message attendu.");
}
}
}
#endregion
}
}
现在,将此属性与命名参数一起使用,而不是“ExpectedException”。使用我的属性,您可以检查是否存在内部异常,异常消息和内部异常,使用正则表达式匹配消息等... 你可以随心所欲地适应。
答案 4 :(得分:4)
在MSTest中没有内置的方法。这就像它的“优雅”一样:
[TestMethod]
public void Test8()
{
var t = new Thrower();
try
{
t.DoStuffThatThrows();
Assert.Fail("Exception expected.");
}
catch (InvalidOperationException e)
{
Assert.AreEqual("Boo hiss!", e.Message);
}
}
但是,您可以考虑将xUnit.NET的Assert.Throws API移植到自定义库中 - 这就是我们所做的。
您还可以转到Microsoft Connect vote on this suggestion。
答案 5 :(得分:2)
MSTest v2支持Assert.Throws和Assert.ThrowsAsync,它们返回捕获的异常。
此处是有关如何升级到MSTest v2的文章:https://blogs.msdn.microsoft.com/devops/2017/09/01/upgrade-to-mstest-v2/
以下是示例用法:
var myObject = new MyObject();
var ex = Assert.Throws<ArgumentNullException>(() => myObject.Do(null));
StringAssert.Contains(ex.Message, "Parameter name: myArg");
答案 6 :(得分:1)
注释和try / catch块的烦恼在于您在测试的ACT和ASSERT阶段之间没有清晰的分离。一个更简单的appraoch是&#34;捕获&#34;作为ACT阶段的一部分,例外情况是使用例如:
public class ViewRenderService : IViewRenderService
{
private readonly IRazorViewEngine _razorViewEngine;
private readonly ITempDataProvider _tempDataProvider;
private readonly IServiceProvider _serviceProvider;
private readonly IHostingEnvironment _env;
private readonly HttpContext _http;
public ViewRenderService(IRazorViewEngine razorViewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider, IHostingEnvironment env, IHttpContextAccessor ctx)
{
_razorViewEngine = razorViewEngine; _tempDataProvider = tempDataProvider; _serviceProvider = serviceProvider; _env = env; _http = ctx.HttpContext;
}
public async Task<string> RenderToStringAsync(string viewName, object model)
{
var actionContext = new ActionContext(_http, new RouteData(), new ActionDescriptor());
using (var sw = new StringWriter())
{
var viewResult = _razorViewEngine.FindView(actionContext, viewName, false);
//var viewResult = _razorViewEngine.GetView(_env.WebRootPath, viewName, false); // For views outside the usual Views folder
if (viewResult.View == null)
{
throw new ArgumentNullException($"{viewName} does not match any available view");
}
var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
{
Model = model
};
var viewContext = new ViewContext(actionContext, viewResult.View, viewDictionary, new TempDataDictionary(_http, _tempDataProvider), sw, new HtmlHelperOptions());
viewContext.RouteData = _http.GetRouteData();
await viewResult.View.RenderAsync(viewContext);
return sw.ToString();
}
}
}
这允许你这样做:
{{1}}
答案 7 :(得分:0)
使用MSTest,你不能这样做。
您已经知道此问题的解决方案:在catch块中声明异常消息。
答案 8 :(得分:0)
MbUnit也可以这样做:
[Test]
[Row(ExpectedExceptionMessage="my message")]
void TestBlah(...
答案 9 :(得分:0)
此代码在异步/等待情况下执行此操作:
public async static Task AssertThrowsAsync<T>(Task task, string expectedMessage) where T : Exception
{
try
{
await task;
}
catch (Exception ex)
{
if (ex is T)
{
Assert.AreEqual(expectedMessage, ex.Message);
return;
}
Assert.Fail($"Expection exception type: {typeof(T)} Actual type: {ex.GetType()}");
}
Assert.Fail($"No exception thrown");
}
示例用法: Code Reference
[TestMethod]
public async Task TestBadRequestThrowsHttpStatusCodeException()
{
var mockHttp = new MockHttpMessageHandler();
const HttpStatusCode statusCode = HttpStatusCode.BadRequest;
mockHttp.When("https://restcountries.eu/rest/v2/")
.Respond(statusCode, "application/json", JsonConvert.SerializeObject(new { Message = "Test", ErrorCode = 100 }));
var httpClient = mockHttp.ToHttpClient();
var factory = new SingletonHttpClientFactory(httpClient);
var baseUri = new Uri("https://restcountries.eu/rest/v2/");
var client = new Client(new NewtonsoftSerializationAdapter(), httpClientFactory: factory, baseUri: baseUri, logger: _logger.Object);
await AssertThrowsAsync<HttpStatusException>(client.GetAsync<List<RestCountry>>(), Messages.GetErrorMessageNonSuccess((int)statusCode, baseUri));
}
这相当于同步情况
public static void AssertThrows<T>(Action action, string expectedMessage) where T : Exception
{
try
{
action.Invoke();
}
catch (Exception ex)
{
if (ex is T)
{
Assert.AreEqual(expectedMessage, ex.Message);
return;
}
Assert.Fail($"Expection exception type: {typeof(T)} Actual type: {ex.GetType()}");
}
Assert.Fail($"No exception thrown");
}
注意:这断言异常是从给定类型继承的。如果要检查特定类型,则应检查类型是否相等,而不要使用is
运算符。
答案 10 :(得分:-1)
更新:糟糕..在MSTest中看到你想要这个。抱歉。速读&amp;被你的头衔误导了。
从Callum Hibbert 尝试此扩展程序,看看它是否有效。
旧回应:
您可以使用NUnit 2.4及更高版本执行此操作。 请参阅此处的ExpectedException documentation
[ExpectedException( typeof( ArgumentException), ExpectedMessage="unspecified", MatchType=MessageMatch.Contains )]
public void TestMethod()
{
...
MatchType可以是Exact(默认),Contains或Regex ..它几乎可以处理80%的用例。如果验证过于复杂,还有一种高级异常处理程序方法方法。从未单独使用过它..但还不需要它。