C# 单元测试接口存储库的目的是什么
我正在对一个C# 单元测试接口存储库的目的是什么,c#,unit-testing,tdd,rhino-mocks,C#,Unit Testing,Tdd,Rhino Mocks,我正在对一个icCustomerRepository接口进行单元测试,该接口用于检索Customer类型的对象 作为单元测试,通过以这种方式测试icCustomerRepository,我获得了什么价值 以下测试在什么情况下会失败 对于这种性质的测试,是否建议进行我知道应该失败的测试?i、 e.当我知道我只在存储库中放置了5时,查找id4 我可能遗漏了一些明显的东西,但实现icCustomerRepository的类的集成测试似乎更有价值 [TestClass] public class C
icCustomerRepository
接口进行单元测试,该接口用于检索Customer
类型的对象
- 作为单元测试,通过以这种方式测试
,我获得了什么价值icCustomerRepository
- 以下测试在什么情况下会失败
- 对于这种性质的测试,是否建议进行我知道应该失败的测试?i、 e.当我知道我只在存储库中放置了
时,查找id5
4
icCustomerRepository
的类的集成测试似乎更有价值
[TestClass]
public class CustomerTests : TestClassBase
{
private Customer SetUpCustomerForRepository()
{
return new Customer()
{
CustId = 5,
DifId = "55",
CustLookupName = "The Dude",
LoginList = new[]
{
new Login { LoginCustId = 5, LoginName = "tdude" },
new Login { LoginCustId = 5, LoginName = "tdude2" }
}
};
}
[TestMethod]
public void CanGetCustomerById()
{
// arrange
var customer = SetUpCustomerForRepository();
var repository = Stub<ICustomerRepository>();
// act
repository.Stub(rep => rep.GetById(5)).Return(customer);
// assert
Assert.AreEqual(customer, repository.GetById(5));
}
}
[TestClass]
公共类CustomerTests:TestClassBase
{
私人客户设置CustomerForrepository()
{
返回新客户()
{
CustId=5,
defid=“55”,
CustLookupName=“这家伙”,
LoginList=new[]
{
新登录名{LoginCustId=5,LoginName=“tdude”},
新登录名{LoginCustId=5,LoginName=“tdude2”}
}
};
}
[测试方法]
公共无效CanGetCustomerById()
{
//安排
var customer=SetUpCustomerForRepository();
var repository=Stub();
//表演
repository.Stub(rep=>rep.GetById(5)).Return(customer);
//断言
AreEqual(customer、repository.GetById(5));
}
}
测试基类
public class TestClassBase
{
protected T Stub<T>() where T : class
{
return MockRepository.GenerateStub<T>();
}
}
公共类TestClassBase
{
受保护的T存根(),其中T:class
{
返回MockRepository.GenerateStub();
}
}
ICustomerRepository和IRepository
public interface ICustomerRepository : IRepository<Customer>
{
IList<Customer> FindCustomers(string q);
Customer GetCustomerByDifID(string difId);
Customer GetCustomerByLogin(string loginName);
}
public interface IRepository<T>
{
void Save(T entity);
void Save(List<T> entity);
bool Save(T entity, out string message);
void Delete(T entity);
T GetById(int id);
ICollection<T> FindAll();
}
公共接口ICCustomerRepository:IRepository
{
IList FindCustomers(字符串q);
Customer GetCustomerByDifID(字符串difId);
客户GetCustomerByLogin(字符串登录名);
}
公共接口假定
{
无效保存(T实体);
作废保存(列表实体);
bool保存(T实体,输出字符串消息);
无效删除(T实体);
T GetById(int-id);
i收集FindAll();
}
根据定义,接口只是契约,因此没有代码可供测试。您希望针对接口的具体实现编写单元测试,因为实际执行代码就存在于此
如果接口没有具体的实现,而您只是在模拟一个具体的实现,那么您的单元测试只是基于一个模拟。这真的没有多大价值。我可能遗漏了一些东西,但似乎测试的每个方面都是模拟的 一般来说,您只模拟不是测试核心的对象。在这种情况下,您可以使用此存储库作为一个函数的源,您希望使用该存储库来检索customer#5并对其执行操作
例如,您可以模拟客户存储库,以便调用验证用户登录的方法。您使用模拟存储库是为了防止单元测试依赖真实数据源,而不是为了测试模拟存储库。您的测试对我来说毫无意义。您正在测试预编程存根是否返回您提供给它的值,而不是测试任何实际代码 遵循dcp的回复。接口是方法的声明。您应该测试这些功能的实现。测试规则#1: 在编写测试之前,请先了解测试的目的以及它试图证明的内容。如果你不知道它证明了什么,那么它是无用的:) 正如其他海报所正确指出的,您正在存根一个接口,然后调用存根——这并不能证明您的生产代码是否工作 什么是存根? 存根用于提供固定值,以驱动被测类的某些方面。例如,假设您有一个
CustomerService
,其实例类型为ICCustomerRepository
。如果您希望看到CustomerService
能够在存储库为空时优雅地处理错误情况,您可以将iccustomerrepository
的GetCustomerById
方法存根为nothing/null/抛出异常,然后确保CustomerService
方法执行了正确的操作(例如,返回未找到客户的结果)
也就是说,存根只是帮助您达到特定条件/行为的合作者。我们正在测试CustomerService
,存根ICCustomerRepository
仅帮助我们实现目标
你不是第一个问这个问题的人:)。我通常建议开发人员这样做。它有助于理解所有的交互以及框架实际上为您做了什么 怎么做?无论我读到哪里,我们都被教导为单元测试模拟接口。我们如何对具体的实现进行单元测试?据我所知,我认为应该避免这样做。你能举个例子吗?非常感谢。接口是一种规范、一种声明,而不是您可以运行(从而测试)的代码。您可以在具体的类中实现该规范,这就是您将要运行和测试的代码。您可以使用模拟来实现接口,以帮助您测试依赖于实现该接口的对象的其他代码。你不是“测试一个接口”,而是通过调用这些实现上的接口方法来测试该接口的实现是否正确(“针对接口进行测试”)。愚蠢的例子:一个
Eater
接口,用于eat
,由Dog
实现,并由提要中的Master
调用(食客宠物)
方法。当您测试Dog
时,您将通过在狗实例上调用eat
来测试Eater
接口的实现,并查看例如狗的体重增加。当您测试Master
sfeed()
方法时,您将创建