【发布时间】:2017-08-15 09:33:36
【问题描述】:
问题已更新,以便更好地解释我遇到的问题,
我有这个控制器,
[Authorize]
public class IdeaManagementController : Controller
{
private IIdeaManagementService _ideaManagementService;
private ITenantService _tenantService;
private ITagService _tagService;
private IEmployeeIdeaCategoryService _ideaManagementCategoryService;
private static PbdModule _modul = PbdModule.IdeaManagement;
IAuthorizationService _authorizationService;
public IdeaManagementController(
IIdeaManagementService ideaManagementService,
ITenantService tenantService,
ITagService tagService,
IAuthorizationService authorizationService,
IEmployeeIdeaCategoryService ideaManagementCategoryService)
{
_ideaManagementService = ideaManagementService;
_tenantService = tenantService;
_tagService = tagService;
_authorizationService = authorizationService;
_ideaManagementCategoryService = ideaManagementCategoryService;
}
public async Task<IActionResult> IdeaCoordinator()
{
if (!await _authorizationService.AuthorizeAsync(User, "IdeaManagement_Coordinator"))
{
return new ChallengeResult();
}
var ideas = _ideaManagementService.GetByIdeaCoordinator(_tenantService.GetCurrentTenantId());
return View(ideas);
}
}
我只需要测试动作方法 IdeaCoordinator 的 retrned viewResult 但我不能简单地因为如果是 _authorizationService.AuthorizeAsync 验证方法,我试图模拟该方法但我不能因为它是一个扩展内置方法,然后我尝试通过创建一个实现 IAuthorizationService 的新接口并模拟该自定义接口来解决解决方案
public interface ICustomAuthorizationService : IAuthorizationService
{
Task<bool> AuthorizeAsync(ClaimsPrincipal user, string policyName);
}
public IAuthorizationService CustomAuthorizationServiceFactory()
{
Mock<ICustomAuthorizationService> _custom = new Mock<ICustomAuthorizationService>();
_custom.Setup(c => c.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), "IdeaManagement_Coordinator")).ReturnsAsync(true);
return _custom.Object;
}
并在我调用控制器构造函数时将其注入,然后我发现自己就是这样:
[Theory]
[InlineData(1)]
public async void IdeaManager_Should_Return_ViewResult(int _currentTenanatID)
{
// Arrange ..
_IdeaManagementControllerObject = new IdeaManagementController
(IdeaManagementServiceMockFactory(_currentTenanatID),
TenantServiceMockFactory(_currentTenanatID),
TagServiceMockFactory(),
AuthorizationServiceMockFactory(),
EmployeeIdeaCategoryServiceMockFactory());
// Act
var _view = await _IdeaManagementControllerObject.IdeaCoordinator() as ViewResult;
// Assert
Assert.IsType(new ViewResult().GetType(), _view);
}
我期待不同的结果,因为我将返回结果标记为 true 只是为了忽略这行代码并继续查看结果,但是当我再次调试我的测试方法时,编译器进入了验证消息,因为它没有感觉到我对 AuthorizeAsync 方法结果所做的更改..
非常感谢您。
==解决方案==
简介:
“我们无法用创造问题的同样水平的思维来解决我们的问题”——阿尔伯特·爱因斯坦。并用这个可爱的说法告诉我,我花了大约 1 周的时间来解决这个问题,直到我觉得现在永远无法解决,我花了几个小时的调查,但是在阅读了一篇文章并改变了思维方式之后,解决方案在 30 分钟内出现。
问题一目了然:
简单地说,我正在尝试对上面编写的操作方法进行单元测试,但我遇到了一个严重的问题,即我无法模拟方法“AuthorizeAsync”,这仅仅是因为它是一个内置的扩展方法并且因为扩展方法本质是静态方法,它永远不能用传统的模拟类的方式来模拟。
详细解决方案:
为了能够模拟此操作方法,我创建了一个包含静态委托的静态类,并使用这些委托进行模拟,或者可以说通过在我的单元测试中替换我的静态委托来“包装”我的扩展方法类如下。
public static class DelegateFactory
{
public static Func<ClaimsPrincipal, object, string, Task<bool>> AuthorizeAsync =
(c, o, s) =>
{
return AuthorizationServiceExtensions.AuthorizeAsync(null, null, "");
};
}
public Mock<IAuthorizationService> AuthorizationServiceMockExtensionFactory()
{
var mockRepository = new Moq.MockRepository(Moq.MockBehavior.Strict);
var mockFactory = mockRepository.Create<IAuthorizationService>();
var ClaimsPrincipal = mockRepository.Create<ClaimsPrincipal>();
mockFactory.Setup(x => x.AuthorizeAsync(It.IsAny<ClaimsPrincipal>(), It.IsAny<object>(), It.IsAny<string>())).ReturnsAsync(true);
return mockFactory;
}
在我的测试方法中,我只是在控制器构造函数实例化中调用了模拟对象。
[Fact]
public async void IdeaCoordinator_When_AuthroizedUser_IsNotNull_But_IdeaManagement_Manager_Authorized_Return_View()
{
// Arrange
int approvedByID = data.GetTenantByID(1).TenantId;
_IdeaManagementControllerObject = new IdeaManagementController
(IdeaManagementServiceMockFactory().Object,
TenantServiceMockFactory().Object,
TagServiceMockFactory().Object,
AuthorizationServiceMockExtensionFactory().Object,
EmployeeIdeaCategoryServiceMockFactory().Object);
//Act
IdeaManagementServiceMockFactory().Setup(m => m.GetByIdeaCoordinator(approvedByID)).Returns(data.GetCordinatedEmpIdeas(approvedByID));
ViewResult _view = await _IdeaManagementControllerObject.IdeaCoordinator() as ViewResult;
var model = _view.Model as List<EmployeeIdea>;
// Assert
Assert.Equal(3, model.Count);
Assert.IsType(new ViewResult().GetType(), _view);
}
正如它所说,幸福的唯一最大原因是感激。我要感谢 Stephen Fuqua 的出色解决方案和文章,http://www.safnet.com/writing/tech/2014/04/making-mockery-of-extension-methods.html
谢谢大家:)!
【问题讨论】:
-
但是您的测试需要一个挑战结果。根据被测方法,当未经授权使用时会发生这种情况。不清楚你在问什么。
-
同时显示控制器构造函数。有很多依赖项必须为测试而模拟。 (代码气味),但需要查看这些依赖项是如何注入的,以便能够为您提供答案
-
该问题已通过编辑控制器的构造函数以及目标模拟类的构造函数进行了更新。
-
我试图模拟这样的方法:_authorizationService.Setup(h => h.AuthorizeAsync(It.IsAny
(), "IdeaManagement_Coordinator")).ReturnsAsync (真的); ...但后来我遇到了一个异常,因为 AuthorizeAsync 是一个静态方法! -
我知道 :( 你对模拟这个方法有什么建议吗?
标签: c# unit-testing asp.net-core-mvc xunit.net asp.net-core-identity