测试 ASP.NET 核心控制器时如何使用 FakeItEasy 动态伪造接口方法
How to fake an Interface method dynamically with FakeItEasy when testing ASP.NET Core controllers
为遵循此模式的 CRUD 控制器编写测试。
- 从action方法参数中获取一个或多个不同类型的参数
- 调用一些IEntityService.GetEntity(来自步骤 1 的参数)方法
- 如果它 return 为 null return NotFound
- 否则return找到的对象
我发现自己反复编写非常相似的测试。
[TestCase(true)]
[TestCase(false)]
public void GetsAccount(bool isExistingAccount)
{
const int accountId = -1;
var account = isExistingAccount ? new Account() : null;
A.CallTo(() => AccountService.GetAccount(accountId)).Returns(account);
var result = (IStatusCodeActionResult)_controller.GetAccount(accountId);
var statusCode = isExistingAccount ? HttpStatusCode.OK : HttpStatusCode.NotFound;
result.StatusCode.Should().Be((int)statusCode);
我试图编写一个通用方法来伪造。
public void GetEntityActionMethodTest<TEntityType>(bool isExisting, Func<int, TEntityType> serviceMethod, Func<int, ActionResult> actionMethod) where TEntityType : class
{
var fakeMethod = A.Fake<Func<int, TEntityType>>();
A.CallTo(() => fakeMethod(-1)).Returns( isExisting ? default(TEntityType) : null);
var result = (IStatusCodeActionResult)actionMethod(-1);
result.StatusCode.Should().Be(isExisting ? (int)HttpStatusCode.OK : (int)HttpStatusCode.NotFound);
}
它有两个问题:
1) 没有正确伪装成 return null
2) 假设接口方法有一个整型参数
问题
1) 创建一个通用方法是否是个好主意,该方法可以使用 FakeItEasy 伪造在不同接口中可能具有不同签名的方法?用反射怎么样?
2) 如果是,我该怎么做?
谢谢。
我不完全确定你的目标是什么。你说你正在尝试编写一个通用方法来伪造,但你的样本看起来像是完整的测试。我会谈谈如何制作假货。将其合并到测试中应该很简单。
如果目标是创建一个假对象,在调用任何方法时将 return 一个特定对象,由假类型和 return 值参数化,您可以使用 FakeItEasy.Sdk namespace methods to create the object and you can configure the fake to respond to any method 像这样:
public object CreateFakeWithReturnValue(Type typeOfFake, object returnValue)
{
var fake = Create.Fake(typeOfFake);
A.CallTo(fake).WithNonVoidReturnType().Returns(returnValue);
return fake;
}
当然,可以通过查看传入的调用来使此方法更复杂(或者如果您知道伪造的类型,则可以使此方法成为通用方法并仅使用标准 A.Fake
建设)。
正如@tom redfern 所说,这可能不是所有情况下的最佳路径,但您必须判断整体方法是否对您有意义。随着测试变得越来越复杂,您可能会发现自己对方法进行了大量扩充,以至于回到 hand-crafted 假货更有意义。
为遵循此模式的 CRUD 控制器编写测试。
- 从action方法参数中获取一个或多个不同类型的参数
- 调用一些IEntityService.GetEntity(来自步骤 1 的参数)方法
- 如果它 return 为 null return NotFound
- 否则return找到的对象
我发现自己反复编写非常相似的测试。
[TestCase(true)]
[TestCase(false)]
public void GetsAccount(bool isExistingAccount)
{
const int accountId = -1;
var account = isExistingAccount ? new Account() : null;
A.CallTo(() => AccountService.GetAccount(accountId)).Returns(account);
var result = (IStatusCodeActionResult)_controller.GetAccount(accountId);
var statusCode = isExistingAccount ? HttpStatusCode.OK : HttpStatusCode.NotFound;
result.StatusCode.Should().Be((int)statusCode);
我试图编写一个通用方法来伪造。
public void GetEntityActionMethodTest<TEntityType>(bool isExisting, Func<int, TEntityType> serviceMethod, Func<int, ActionResult> actionMethod) where TEntityType : class
{
var fakeMethod = A.Fake<Func<int, TEntityType>>();
A.CallTo(() => fakeMethod(-1)).Returns( isExisting ? default(TEntityType) : null);
var result = (IStatusCodeActionResult)actionMethod(-1);
result.StatusCode.Should().Be(isExisting ? (int)HttpStatusCode.OK : (int)HttpStatusCode.NotFound);
}
它有两个问题:
1) 没有正确伪装成 return null
2) 假设接口方法有一个整型参数
问题
1) 创建一个通用方法是否是个好主意,该方法可以使用 FakeItEasy 伪造在不同接口中可能具有不同签名的方法?用反射怎么样?
2) 如果是,我该怎么做?
谢谢。
我不完全确定你的目标是什么。你说你正在尝试编写一个通用方法来伪造,但你的样本看起来像是完整的测试。我会谈谈如何制作假货。将其合并到测试中应该很简单。
如果目标是创建一个假对象,在调用任何方法时将 return 一个特定对象,由假类型和 return 值参数化,您可以使用 FakeItEasy.Sdk namespace methods to create the object and you can configure the fake to respond to any method 像这样:
public object CreateFakeWithReturnValue(Type typeOfFake, object returnValue)
{
var fake = Create.Fake(typeOfFake);
A.CallTo(fake).WithNonVoidReturnType().Returns(returnValue);
return fake;
}
当然,可以通过查看传入的调用来使此方法更复杂(或者如果您知道伪造的类型,则可以使此方法成为通用方法并仅使用标准 A.Fake
建设)。
正如@tom redfern 所说,这可能不是所有情况下的最佳路径,但您必须判断整体方法是否对您有意义。随着测试变得越来越复杂,您可能会发现自己对方法进行了大量扩充,以至于回到 hand-crafted 假货更有意义。