2010-10-17 88 views
4

我剛開始用的單元測試播放嘲笑使用/起訂量,並遇到了一個問題..C#+模擬服務層?

我已經下面的代碼名爲「爲CustomerService」服務層:

public interface ICustomerService 
{ 
    Customer GetCustomerById(int id); 
} 

public class CustomerService : ICustomerService 
{ 
    private IRepository<Customer> customerRepository; 

    public CustomerService(IRepository<Customer> rep) 
    { 
     customerRepository = rep; 
    } 
    public Customer GetCustomerById(int id) 
    { 
     var customer = customerRepository.Get(x => x.CustomerId == id); 

     if (customer == null) 
      return null; 

     return customer; 
    } 
} 

我的倉庫類是通用的,是以下幾點:

public interface IRepository<T> : IDisposable where T : class 
    { 
     T Get(Expression<Func<T, bool>> predicate); 
    } 

    public class Repository<T> : IRepository<T> where T : class 
    { 
     private ObjectContext context; 
     private IObjectSet<T> objectSet; 

     public Repository() 
      : this(new demonEntities()) 
     { 
     } 

     public Repository(ObjectContext ctx) 
     { 
      context = ctx; 
      objectSet = context.CreateObjectSet<T>(); 
     } 

     public T Get(Expression<Func<T, bool>> predicate) 
     { 
      T entity = objectSet.Where<T>(predicate).FirstOrDefault(); 

      if (entity == null) 
       return null; 

      return objectSet.Where<T>(predicate).FirstOrDefault(); 
     } 

     public void Dispose() 
     { 
      Dispose(true); 
      GC.SuppressFinalize(this); 
     } 

     protected virtual void Dispose(bool disposing) 
     { 
      if (disposing) 
      { 
       if (context != null) 
       { 
        context.Dispose(); 
        context = null; 
       } 
      } 
     } 
    } 

現在是我的問題。我怎樣才能讓單元測試來檢查我是否GetCustomerById返回空或不是?

已經嘗試過:

[TestMethod] 
public void GetCustomerTest() 
{ 
    const int customerId = 5; 

    var mock = new Mock<IRepository<Customer>>(); 
    mock.Setup(x => x.Get(z => z.CustomerId == customerId)) 
     .Returns(new Customer()); 

    var repository = mock.Object; 
    var service = new CustomerService(repository); 
    var result = service.GetCustomerById(customerId); 

    Assert.IsNotNull(result); 
} 

沒有運氣...

+0

通過傳遞模擬庫。使用不在模擬存儲庫中的ID進行調用。順便說一句,這是沒有太多的測試.... – 2010-10-17 11:09:42

+0

已經嘗試過,沒有運氣... – ebb 2010-10-17 11:12:13

回答

5

你需要讓Repository<T>.Get方法虛擬的,這樣起訂量可以覆蓋它並返回您設置的值:

public virtual T Get(Expression<Func<T, bool>> predicate) 

,並在您的測試,改變

mock.Setup(x => x.Get(z => z.CustomerId == customerId)) 
     .Returns(new Customer()); 

mock.Setup(x => x.Get(It.IsAny<Expression<Func<Customer, bool>>>())) 
     .Returns(new Customer()); 

哪些說返回一個新的Customer任何Expression<Func<Customer, bool>> pas最好你會測試一個特定的表達式,但根據這個SO question的接受答案,Moq不能做到這一點。

如果你想測試你的服務層,未進行任何意外由庫返回的Customer,而不是測試,看看這任何Customer被退回,你可以建立一個模擬Customer(即一定要使CustomerId屬性爲虛擬)並聲明服務層返回的Customer具有預期的屬性。

[TestMethod] 
public void GetCustomerTest() 
{ 
    const int customerId = 5; 

    var mockCustomer = new Mock<Customer>(); 

    mockCustomer.SetupGet(x => x.CustomerId) 
     .Returns(customerId); 

    var mock = new Mock<IRepository<Customer>>(); 

    mock.Setup(x => x.Get(It.IsAny<Expression<Func<Customer, bool>>>())) 
     .Returns(mockCustomer.Object); 

    var repository = mock.Object; 
    var service = new CustomerService(repository); 
    var result = service.GetCustomerById(customerId); 

    Assert.AreEqual(customerId, result.CustomerId); 
} 

HTH

+0

我收到第22行的錯誤:「測試方法MyApp.Test.Services.CustomerServiceTest.GetCustomerTest引發異常: System.ArgumentException:非可覆蓋成員上的無效安裝: x => x.CustomerId」 – ebb 2010-10-17 17:42:55

+0

檢查爲了確保CustomerId是虛擬的,那麼它應該沒問題 – 2010-10-17 18:00:06

+0

因此我必須從EF編輯生成的代碼? – ebb 2010-10-17 18:01:54

0

你必須創建對接口的模擬。

然後需要在類中設置成員的模擬,而不是實現者

然後需要調用.Setup你的方法創建的模擬.. rememver使用方法鏈,使其驗證。

在您的測試運行的方法,然後調用mock.Verify()如果​​你需要

將添加代碼的明天。我在工作中可以使用大量代碼示例。

+0

嗯..這幾乎已經是我所做的atm(如果你看我的帖子,我已經更新了一些更多的代碼) ... – ebb 2010-10-17 11:18:48

+0

從你的更新和其他意見,我無法幫助。對解決方案感興趣雖然我放棄了當我嘗試類似的東西。 – 2010-10-17 21:45:34

0

你不能這樣做,這是因爲一個拉姆達x => x.CustomerId == id不等於另一個lambda x => x.CustomerId == id所以起訂量不能滿足他們的原因。

但是,如果你有,比如說,在與您共同操作的類:

public class CustomerQueries { 
    public static Predicate<Customer> ById(int id) = x => x.CustomerId == id; 
} 

和再利用這個拉姆達在代碼和測試,那麼你的起訂量應該通過沒有任何問題。您也可以在類的已知實例上使用方法,只要它是完全相同的代理,而不僅僅是類似的副本。

PS:你有沒有考慮過使用Predicate<T>而不是Expression<Func<T, bool>>?閱讀和理解目的更容易。