2010-10-17 73 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>>?阅读和理解目的更容易。