Reputation: 663
Writing tests for CRUD controllers which follow this pattern.
I found myself writing very similar tests repeatedly.
[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);
I attempted to write a generic method to fake.
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);
}
There are two issues with it:
1) Does not fake correctly to return null
2) Assumes interface method has one integer parameter
Questions
1) Is it a good idea to work on creating a generic method that can fake methods that may have different signatures in different interfaces using FakeItEasy? How about with reflection?
2) If yes, how can I do that?
Thanks.
Upvotes: 0
Views: 2530
Reputation: 241714
I'm not entirely sure what your goal is. You say you're trying to write a generic method to fake, but your sample looked like the complete test. I'll talk about how to make the fake. Incorporating it into the tests should be straightfoward.
If the goal is to create a fake that will return a particular object when any method is called, parameterized by the fake type and the return value, you could use the FakeItEasy.Sdk namespace methods to create the object and you can configure the fake to respond to any method like so:
public object CreateFakeWithReturnValue(Type typeOfFake, object returnValue)
{
var fake = Create.Fake(typeOfFake);
A.CallTo(fake).WithNonVoidReturnType().Returns(returnValue);
return fake;
}
Of course, this method could be made more sophisticated by looking at the call that's passed in (or if you know the type of the fake, you could make this method a generic and just use the standard A.Fake
construction).
As @tom redfern says, this may not be the best path in all cases, but you'll have to judge whether the overall approach makes sense to you. As tests get more sophisticated, you may find yourself augmenting the method quite a lot, to the point where it just makes more sense to go back to hand-crafted fakes.
Upvotes: 4