C# +模拟服务层?
我刚刚开始使用 Moq 进行单元测试/模拟,并遇到了问题。
我有一个名为“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 是否返回 null?
已经尝试过:
[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);
}
没有运气......
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您需要将
Repository.Get
方法设为虚拟,以便 Moq 可以覆盖它并返回您设置的值:并在测试中更改
为
返回新的
Customer< /code> 对于任何传入的
Expression>
。理想情况下,您会测试特定表达式,但根据对此 SO问题,Moq无法做到这一点。如果您想测试您的服务层是否没有对存储库返回的
Customer
执行任何意外操作,而不是测试以查看任何Customer
> 返回后,您可以设置一个模拟Customer
(确保将CustomerId
属性设置为虚拟)并断言返回的Customer
服务层具有预期的属性。华泰
You need to make the
Repository<T>.Get
method virtual so Moq can override it and return the value you set up:and in your test, change
to
which says return a new
Customer
for anyExpression<Func<Customer, bool>>
passed in. Ideally you would test a specific expression, but per the accepted answer to this SO question, Moq cannot do this.If you wanted to test that your service layer was not doing anything unexpected to the
Customer
returned by the repository, instead of testing to see that anyCustomer
was returned, you could set up a mockCustomer
(being sure to make theCustomerId
property virtual) and assert that theCustomer
returned by the service layer had the expected properties.HTH
您必须针对界面创建模拟。
然后,您需要将类中的成员设置为模拟而不是实现者
,然后您需要在为方法创建的模拟上调用 .Setup .. rememver 以使用方法链接使其可验证。
在测试中运行该方法,然后调用mock.Verify()
如果需要,明天将添加代码。我在工作中有大量可以使用的代码示例。
you have to create the mock against the interface.
you then need to set the member in the class to the mock instead of the implementor
you then need to call .Setup on the mock you created for the method .. rememver to use method chaining to make it verifiable.
run the method in your test and then call mock.Verify()
Will add code tomorrow if you require. I have loads of code samples at work i can use.
你不能这样做的原因是因为一个 lambda
x =>; x.CustomerId == id
不等于另一个 lambdax =>; x.CustomerId == id
因此 Moq 无法匹配它们。但是,如果您有一个类,其常见操作为:
并在代码和测试中重用此 lambda,那么您的 Moq 应该可以顺利通过。您也可以在类的已知实例上使用一种方法,只要它是完全相同的委托而不仅仅是相似的一个或另一个副本。
PS:您是否考虑过使用
Predicate
代替Expression>
?更容易阅读和理解其目的。The reason you can't do this is because one lambda
x => x.CustomerId == id
is not equal to another lambdax => x.CustomerId == id
so Moq can't match them.However, if you had, say, a class with your common operations on:
and reused this lambda in both code and test, then your Moq should pass without any problem. You could alternatively use a method on a known instance of a class, as long as it's exactly the same delegate and not merely a similar one or another copy.
PS: Have you considered using
Predicate<T>
instead ofExpression<Func<T, bool>>
? It's easier to read and to understand the purpose.