I have the following (here simplified) code which I want to test with FakeItEasy.
public class ActionExecutor : IActionExecutor
{
public void TransactionalExecutionOf(Action action)
{
try
{
// ...
action();
// ...
}
catch
{
// ...
Rollback();
}
}
public void Commit()
{ }
public void Rollback()
{ }
}
public class Service : IService
{
private readonly IRepository _repository;
private readonly IActionExecutor _actionExecutor;
// ctor for CI
public void ServiceMethod(string name)
{
_actionExecutor.TransactionalExecutionOf(() =>
{
var item = _repository.FindByName(ItemSpecs.FindByNameSpec(name));
if (item == null) throw new ServiceException("Item not found");
item.DoSomething();
_actionExecutor.Commit();
}
}
}
I want to test that the ServiceException
is thrown so i setup my test like that
var repo = A.Fake<IRepository>();
A.CallTo(() => repo.FindByName(A<ISpec<Item>>.Ignored))
.Returns(null);
var executor = A.Fake<IActionExecutor>();
executor.Configure()
.CallsTo(x => x.Rollback()).DoesNothing();
executor.Configure()
.CallsTo(x => x.Commit()).DoesNothing();
executor.Configure()
.CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored))
.CallsBaseMethod();
With the following code
var service = new Service(executor, repo);
service.ServiceMethod("notExists")
.Throws(new ServiceException());
I get the following message
The current proxy generator can not intercept the specified method for the following reason: - Sealed methods can not be intercepted.
If I call the method directly on the service like
var service = new Service(executor, repo);
service.ServiceMethod("NotExists");
I get this message
This is a DynamicProxy2 error: The interceptor attempted to 'Proceed' for method 'Void TransactionalExecutionOf(System.Action)' which has no target. When calling method without target there is no implementation to 'proceed' to and it is the responsibility of the interceptor to mimic the implementation (set return value, out arguments etc)
Now I am a bit confused and don't know what to do next.
Problems comes from the way you create fake and what you later expect it to do:
What base method? FakeItEasy has no idea what the base class is, and hence the
DynamicProxy2
exception in your second case. You can create partial mock this way:Note that we're basing on actual implementation, not interface anymore
This however introduces a new set of problems, as methods on
ActionExecutor
are not virtual and therefore interceptor cannot hook up to well - intercept them. To make your current setup work, you'll have to change yourActionExecutor
and make (all) the methods virtual.However, you may (or even should) want to avoid modifications of existing code (which sometimes might not even be an option). You could then set up your
IActionExecutor
fake like this:This will allow you to work on faked object, with the exception of call to
TransactionalExecutionOf
which will be redirected to actual implementation.