2012-01-29 41 views
3

我有以下(這裏簡化)的代碼,我想用FakeItEasy進行測試。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(); 
     } 
    } 
} 

我想測試的ServiceException被拋出,所以我安裝我的測試一樣,

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(); 

用下面的代碼

var service = new Service(executor, repo); 
service.ServiceMethod("notExists") 
     .Throws(new ServiceException()); 

我得到以下信息

The current proxy generator can not intercept the specified method for the following reason: - Sealed methods can not be intercepted.

如果我直接調用該服務的方法類似

var service = new Service(executor, repo); 
service.ServiceMethod("NotExists"); 

我得到這個消息

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)

現在我有點迷茫,不知道下一步該怎麼做。

回答

6

問題來自於你製造假的方式,你以後希望它做的事:

var executor = A.Fake<IActionExecutor>(); 
// ... 
executor.Configure() 
    .CallsTo(x => x.TransactionalExecutionOf(A<Action>.Ignored)) 
    .CallsBaseMethod(); 

什麼基方法? FakeItEasy不知道基類是什麼,因此在第二種情況下爲DynamicProxy2例外。您可以創建部分模擬這樣:

var executor = A.Fake<ActionExecutor>(); 

注意,我們立足實際執行,而不是接口了

然而,這引入了一個新的問題,如在ActionExecutor方法不是虛擬的,因此攔截器不能很好地攔截它們。爲了使你當前的設置工作,你必須改變你的ActionExecutor,並使(全部)虛擬方法。

但是,您可能(甚至應該)想要避免修改現有代碼(有時甚至可能不是一個選項)。然後,您可以設置您的IActionExecutor假像這樣:

var executor = A.Fake<IActionExecutor>(); 
A.CallTo(() => executor.TransactionalExecutionOf(A<Action>.Ignored)) 
    .Invokes(f => new ActionExecutor() 
     .TransactionalExecutionOf((Action)f.Arguments.First()) 
    ); 

這將允許你僞造的對象上工作,與呼叫例外TransactionalExecutionOf將被重定向到實際執行。

+0

如果我按照實現方式和'virtual'的方式工作,但我不想將實現更改爲虛擬。我的方式/我的目標(太)錯了嗎?有沒有其他的框架/庫來做這樣的事情? – Khh 2012-01-29 15:36:01

+1

@ K.霍夫曼:不,這對於避免修改現有代碼是完全合理的。我已經添加了解決方法,以便您可以以不同方式進行測試。據我所知,大多數免費的嘲諷庫不允許這樣的事情(攔截非虛擬方法),因爲它們都依賴於DynamicProxy和類似的機制。 – 2012-01-29 15:38:16

+0

我嘗試編輯 – Khh 2012-01-29 15:47:36