问题描述
某些上下文:
我正在对一个简单的MyController
进行单元测试,对于每项测试,我都断言http响应对象是Microsoft.AspNetCore.Mvc.ObjectResult
基类的特定类型或其任何派生类,例如{ {1}}或BadRequestObjectResult
。
OkObjectResult
例如,在每个单元测试结束时,我断言实际的返回类型为MyController : BaseController
{
public void IActionResult> Get(...)
{
try
{
if (...) retrun BadRequest(...)
return Ok(...);
}
catch (Exception ex)
{
return StatusCode(500,...);
}
}
,其中ObjectResult
为500,或者第二个单元测试的返回类型为{{1} },其中HttpCode
为400:
BadRequestObjectResult
这里是用于类型转换和声明HttpCode
的断言功能:
[Test]
public void ControllerTest_WhenFail_ShouldRetrunInternalServerError()
{
...
IActionResult actual = controller.Get(...);
//Assert
AssertHttpCode<ObjectResult>(actual,500);
}
public void ControllerTest_WhenRequestNotValid_ShouldRetrunBadRequest()
{
...
IActionResult actual = controller.Get(...);
//Assert
AssertHttpCode<BadRequestObjectResult>(actual,400);
}
我的问题:
而不是从每个单元测试中发送StatusCode
作为参数,我想知道是否有可能解析运行private void AssertHttpCode<T>(IActionResult actual,int httpCode) where T : ObjectResult
{
Assert.IsNotNull(actual);
T objectResult = actual as T;
Assert.AreEqual(objectResult.StatusCode,httpCode);
}
的{{1}} -time T值?,因此httpCode
函数的客户端将不需要发送StatusCode
作为参数,例如:
ObjectResult
解决方法
否,由于ObjectResult类型与状态代码之间没有一对一的关系,因此无法确定某个ObjectResult派生类型将其分配给其StatusCode property的状态代码。
因此,一个实现可以具有多个代码路径,这些路径根据某些条件分配不同的状态代码,另一个实现可以(仅)具有用户提供的状态代码,并且 some 始终返回相同的代码(除非从用户代码)。
此外,您的测试应明确显示期望的输出(通常的格式也为Assert(expected,actual)
,因此请切换这些参数)。如果某些框架更新更改了某个响应类型的状态代码怎么办?您的测试将接受该类型使用的状态代码,因此当API在响应中提供不同的状态代码时,测试不会中断。
如果要检查的对象结果类型具有无参数构造函数,则可以将其添加到约束中并执行类似的操作。
private void AssertHttpCode<T>(IActionResult actual) where T : ObjectResult,new()
{
Assert.IsNotNull(actual);
T objectResult = actual as T;
var t = new T();
Assert.AreEqual(objectResult.StatusCode,t.StatusCode);
}
否则,您将必须进行一些模式匹配以查看其类型。
我相信大多数ObjectResult
类型只需要传递一个对象即可创建它们。您可以试试看。
private void AssertHttpCode<T>(IActionResult actual) where T : ObjectResult
{
Assert.IsNotNull(actual);
T objectResult = actual as T;
var t = Activator.CreateInstance(typeof(T),new object()) as T;
Assert.AreEqual(objectResult.StatusCode,t.StatusCode);
}
您可以为500个错误创建一个新类,以在注释中解决您的问题。
public class InternalServerErrorObjectResult : ObjectResult
{
public InternalServerErrorObjectResult(object value) : base(value)
{
StatusCode = 500;
}
}
,
最终,我发现Type-Pattern
(doc)在这种情况下非常方便(感谢@CamiloTerevinto作为提示):
private void AssertHttpCode<T>(IActionResult actual) where T : ObjectResult
{
Assert.IsNotNull(actual);
T actualObjectResult = actual as T;
switch (actualObjectResult)
{
case BadRequestObjectResult x:
Assert.AreEqual(400,actualObjectResult.StatusCode);
break;
case OkObjectResult x:
Assert.AreEqual(200,actualObjectResult.StatusCode);
break;
case UnauthorizedObjectResult x:
Assert.AreEqual(401,actualObjectResult.StatusCode);
break;
case CreatedAtActionResult x:
Assert.AreEqual(201,actualObjectResult.StatusCode);
break;
case ObjectResult x:
Assert.AreEqual(500,actualObjectResult.StatusCode);
break;
default:
throw new NotImplementedException();
}
}
这也回答了@codecaster的答案引起的关注:
如果某些框架更新更改了某些设备的状态码该怎么办 回应类型?您的测试将接受该类型的状态码 使用,因此您的测试不会中断,而您的API将提供不同的 状态码。
在框架更新的情况下,对断言进行硬编码会使单元测试中断。