我正在尝试模拟一个注册用户进行测试的类。在测试代码中,我可以明显看到它在下面的 callbackUrl 上失败了。
PageModel类具有一个声明为Url的字段IUrlHelper。 IUrlHelper界面有5种方法,不包括.Page()。模拟这5个将很容易,但是我不知道如何模拟扩展方法。
有人可以帮忙吗?我已经坚持了好多年了。
public class RegisterModel : PageModel
{
private readonly IUrlHelper _urlHelper;
public RegisterModel(
IUrlHelper urlHelper)
{}
public async Task<IActionResult> OnPostAsync(
string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
var callbackUrl = Url.Page(
"/Account/ConfirmEmail",
pageHandler: null,
values: new { userId = "full code has IdentityUserCreated", code = "string" },
protocol: Request.Scheme);
LocalRedirect(returnUrl);
return Page();
}
}
[TestFixture]
public class RegisterModelTests
{
private Mock<IUrlHelper> _mockUrlHelper;
[SetUp]
public void SetUp()
{
_mockUrlHelper = new Mock<IUrlHelper>();
SetUpUrlHelper();
}
public RegisterModel CreateRegisterModel()
{
return new RegisterModel(
_mockUrlHelper.Object
);
}
[Test]
public async Task GivenValidInput_OnPostAsync_CreatesANewUser()
{
// Arrange
var unitUnderTest = CreateRegisterModel();
// Act
var result = await unitUnderTest.OnPostAsync("/asdsad/asda");
// Assert
if (result != null)
Assert.Pass();
}
private void SetUpUrlHelper()
{
_mockUrlHelper.Setup(x => x.Page(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IdentityUser>(),
It.IsAny<string>())).Returns("callbackUrl").Verifiable();
}
答案 0 :(得分:1)
简而言之,您不能模拟扩展方法,因为它们是静态方法,moq仅能处理对象。但是this post会告诉您更多信息。
您可能需要更改测试方式,将其移至控制器级别。但是我认为有一个技术解决方案。我们可以做到,但也许我们不应该这样做。
但是,您应该能够填充该方法。它正在与另一个交换方法地址。确保您确实需要它,并确保它仅在测试中使用。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
public static class ShimHelper
{
public static void Replace<TOriginal, TTarget>()
{
var typeOfOriginal = typeof(TOriginal);
Replace<TTarget>(typeOfOriginal);
}
public static void Replace<TTarget>(Type typeOfOriginal)
{
var targetMethods = GetStaticPublicMethods<TTarget>();
foreach (var targetMethod in targetMethods)
{
var parameters = targetMethod.GetParameters().Select(x => x.ParameterType).ToArray();
var originalMethod = typeOfOriginal.GetMethod(targetMethod.Name, parameters);
if (originalMethod != null)
{
SwapMethodBodies(originalMethod, targetMethod);
}
else
{
Debug.WriteLine(
"*****************************************************************************************");
Debug.WriteLine($"Method not found - {targetMethod.Name}");
Debug.WriteLine(
"*****************************************************************************************");
}
}
}
private static List<MethodInfo> GetStaticPublicMethods<T>()
{
return typeof(T).GetMethods(BindingFlags.Public | BindingFlags.Static)
.Distinct().ToList();
}
private static void SwapMethodBodies(MethodInfo a, MethodInfo b)
{
RuntimeHelpers.PrepareMethod(a.MethodHandle);
RuntimeHelpers.PrepareMethod(b.MethodHandle);
unsafe
{
if (IntPtr.Size == 4)
{
Replace32Bit(a, b);
}
else
{
Replace64Bit(a, b);
}
}
}
private static unsafe void Replace64Bit(MethodInfo a, MethodInfo b)
{
var inj = (long*)b.MethodHandle.Value.ToPointer() + 1;
var tar = (long*)a.MethodHandle.Value.ToPointer() + 1;
*tar = *inj;
}
private static unsafe void Replace32Bit(MethodInfo a, MethodInfo b)
{
var inj = (int*)b.MethodHandle.Value.ToPointer() + 2;
var tar = (int*)a.MethodHandle.Value.ToPointer() + 2;
*tar = *inj;
}
}
用法:
ShimHelper.Replace<ExtensionClass, MockedExtensionClass>();
您的模拟扩展类在哪里与方法签名完全匹配。在您的测试装置设置中运行此程序,您应该会很好。
答案 1 :(得分:0)
我尝试了ICodeGorilla的解决方案,但发现静态类型不能用作类型参数。因此,我对此进行了一些修改:
public static void Replace(Type original, Type target)
{
var targetMethods = GetStaticPublicMethods(target);
foreach (var targetMethod in targetMethods)
{
var parameters = targetMethod.GetParameters().Select(x => x.ParameterType).ToArray();
var originalMethod = original.GetMethod(targetMethod.Name, parameters);
if (originalMethod != null)
{
SwapMethodBodies(originalMethod, targetMethod);
}
else
{
Debug.WriteLine(
"*****************************************************************************************");
Debug.WriteLine($"Method not found - {targetMethod.Name}");
Debug.WriteLine(
"*****************************************************************************************");
}
}
}
private static List<MethodInfo> GetStaticPublicMethods(Type t)
{
return t.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Distinct().ToList();
}
现在的用法是:
ShimHelper.Replace(
typeof(ExtensionClass),
typeof(MockedExtensionClass));
我发现这对于MVC中的AjaxRequestExtensions非常有效。