我有一个带有控制器的ASP.NET核心MVC API,需要进行单元测试。
控制器:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace TransitApi.Api.Controllers
{
[Route("api/foo")]
public class FooController : Controller
{
private IFooRepository FooRepository { get; }
public FooController(IFooRepository fooRepository)
{
FooRepository = fooRepository;
}
[HttpGet]
[Authorize("scopes:getfoos")]
public async Task<IActionResult> GetAsync()
{
var foos = await FooRepository.GetAsync();
return Json(foos);
}
}
}
我必须能够对AuthorizeAttribute
的有效性进行单元测试。我们的代码库中存在缺少属性和不正确范围的问题。 This answer正是我要找的,但ActionInvoker
中没有Microsoft.AspNetCore.Mvc.Controller
方法意味着我无法这样做。
单元测试:
[Fact]
public void GetAsync_InvalidScope_ReturnsUnauthorizedResult()
{
// Arrange
var fooRepository = new StubFooRepository();
var controller = new FooController(fooRepository)
{
ControllerContext = new ControllerContext
{
HttpContext = new FakeHttpContext()
// User unfortunately not available in HttpContext
//,User = new User() { Scopes = "none" }
}
};
// Act
var result = controller.GetAsync().Result;
// Assert
Assert.IsType<UnauthorizedResult>(result);
}
如何对没有正确范围的用户拒绝访问我的控制器方法进行单元测试?
目前我已经决定只测试AuthorizeAttribute
的存在如下,但这实际上不够好:
[Fact]
public void GetAsync_Analysis_HasAuthorizeAttribute()
{
// Arrange
var fooRepository = new StubFooRepository();
var controller = new FooController(fooRepository)
{
ControllerContext = new ControllerContext
{
HttpContext = new FakeHttpContext()
}
};
// Act
var type = controller.GetType();
var methodInfo = type.GetMethod("GetAsync", new Type[] { });
var attributes = methodInfo.GetCustomAttributes(typeof(AuthorizeAttribute), true);
// Assert
Assert.True(attributes.Any());
}
答案 0 :(得分:7)
这需要与内存测试服务器进行集成测试,因为在处理请求管道时,框架会评估该属性。
Integration testing in ASP.NET Core
集成测试可确保应用程序组件在组装在一起时正常运行。 ASP.NET Core支持使用单元测试框架和内置测试Web主机进行集成测试,可以在没有网络开销的情况下处理请求。
[Fact]
public async Task GetAsync_InvalidScope_ReturnsUnauthorizedResult() {
// Arrange
var server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
var client = server.CreateClient();
var url = "api/foo";
var expected = HttpStatusCode.Unauthorized;
// Act
var response = await client.GetAsync(url);
// Assert
Assert.AreEqual(expected, response.StatusCode);
}
如果您不希望测试达到实际的生产实现,您还可以专门为测试创建一个启动,以替换存根/模拟的DI的任何依赖项。
答案 1 :(得分:0)
您可以做的是配置您的测试服务器以添加一个匿名过滤器中间件:
private HttpClient CreatControllerClient()
{
return _factory.WithWebHostBuilder(builder
=> builder.ConfigureTestServices(services =>
{
// allow anonymous access to bypass authorization
services.AddMvc(opt => opt.Filters.Add(new AllowAnonymousFilter()));
})).CreateClient();
}