我首先要说的是我对单元测试相当新,但我现在是一个坚定的信徒。我以前的工作总是有一个测试员所以我从来不用担心这个。
我有一个替换Web服务的ASP.Net MVC API。 API从Web应用程序获取HTTP Post,将请求记录到该数据库,然后发出请求并从另一个数据库返回数据。它正在替换的Web服务有额外的开销,即确保随请求一起提交的用户名是登录用户。要替换它,我使用站点身份验证和关联控制器的User.Identity对象。
现在我需要为此编写单元测试。我可以使用Moq为MVC控制器模拟经过身份验证的用户,但似乎无法找到如何为API验证经过身份验证的用户。这是我想要做的:
... // In the test method
DbApiController controller = new DbApiController ();
controller.ControllerContext = GetMockContext();
...
private HttpControllerContext GetMockContext()
{
var identity = new GenericIdentity("testuser");
var controllerContext = new Mock<HttpControllerContext>();
var principal = new Mock<IPrincipal>();
principal.Setup(p => p.IsInRole("SalesAndEstimating"));
principal.SetupGet(p => p.Identity.Name).Returns("testuser");
controllerContext.SetupGet(c => c.RequestContext.Principal).Returns(principal.Object);
return controllerContext.Object;
}
我得到了例外:
System.NotSupportedException: Invalid setup on a non-virtual (overridable in VB) member: mock => mock.RequestContext
我在这里做错了什么?
更新代码:
ApiController controller = new DbApiController();
var context = new Mock<HttpContextBase>();
var identity = new GenericIdentity("testuser");
identity.AddClaim(new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "1"));
var principal = new GenericPrincipal(identity, new[] { "user" });
context.Setup(s => s.User).Returns(principal);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
var results = ((DbApiController)controller).Post(...);
Assert.AreEqual(results.Success, true);
在我进行上述更改后,我仍然收到错误“Argument 3:无法从'System.Web.Http.ApiController'转换为'System.Web.Mvc.ControllerBase'”。
答案 0 :(得分:2)
我看到这一点非常多,而且在单元测试方面,以及应该或不应该测试什么以及如何进行测试时,似乎存在很大的误解。
您可以对单元测试功能,业务规则以及以某种方式更改数据的任何内容进您没有对控制器进行单元测试。
理想情况下,您希望控制器非常简单,接受请求,调用一些服务来完成任何需要的操作并返回结果。控制器应该非常轻。如果您这样编码,那么您不需要对它们进行单元测试,您可以对实际执行某些操作的服务进行单元测试。
现在,您仍然需要测试并确保您的API正常工作,因此下一步是为您的终端编写集成测试,这就像Postman这样的东西有帮助。该工具还具有可与CI服务器集成的优势,这使得集成测试非常容易添加到您的管道中。
简而言之,想想你的单元测试,单元测试功能,不要尝试测试你正在使用的框架,这是完成的,应该由编写框架的人来完成。
当然使用模拟,但是如果你模拟请求并模拟数据库中的响应,这并不意味着你的代码有效,那么即使你的测试通过,数据库也可以以不同的方式工作并抛出错误。真的让你的代码更好。
单元测试应该是小型的,专注的,它们不会触及任何外部系统,如磁盘上的文件,数据库,其他API。这样做的原因是你希望它们运行得非常快,即使你有数千个,它们需要在几秒或更短的时间内运行,这样你就可以一直运行它们。作为开发人员,你应该一直运行它们以确保你没有破坏任何东西。
不要为了增加任意数字而陷入添加测试的陷阱。
答案 1 :(得分:1)
您无法设置RequestContext
,因为它不是虚拟的,因此无法被Mock覆盖。
您可以使用此代码
var context = new Mock<HttpContextBase>();
var identity = new GenericIdentity("testuser");
identity.AddClaim(new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", "1"));
var principal = new GenericPrincipal(identity, new[] { "user" } );
context.Setup(s => s.User).Returns(principal);
ApiController controller = new ApiController();
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
答案 2 :(得分:1)
受到Kahbazi实施的启发,有一条稍微容易的路线。不过,这是AspNetCore。
// set up identity and required claims
var identity = new GenericIdentity("username");
identity.AddClaim(new Claim("<your-type>", "<your-value>"));
// set up claims principal
var principal = new ClaimsPrincipal(identity);
// set up httpcontext
var context = new DefaultHttpContext()
{
User = principal
};
// associate httpcontext to controller
controller.ControllerContext = new ControllerContext
{
HttpContext = context
};