Alper
Alper

Reputation: 663

How to fake an Interface method dynamically with FakeItEasy when testing ASP.NET Core controllers

Writing tests for CRUD controllers which follow this pattern.

  1. Get one or more parameters of various types from action method parameters
  2. Call some IEntityService.GetEntity(parameters from step 1) method
  3. If it returns null return NotFound
  4. Otherwise return the found object

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

Answers (1)

Blair Conrad
Blair Conrad

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

Related Questions