使用RhinoMocks模拟table进行非单元测试
本文关键字:单元测试 table RhinoMocks 模拟 使用 | 更新日期: 2023-09-27 18:06:57
我有一个接口,我运行一个linq到sql查询:
public interface IMyDataContext : IDisposable
{
ITable<MyTable> GetMyTable();
}
在这个界面上,我正在运行linq查询:
var results = from table1 in _MyDataContext.GetMyTable()
group table1 by table1.Column1 into myGroup
orderby myGroup.Count() descending
select new
{
Column1 = myGroup.Key,
Count = myGroup.Count()
};
查询运行正常。在编写单元测试时,我陷入了困境。如何获得函数GetMyTable()返回一个模拟对象与一些假数据,围绕这里的待办事项:
public class MockMyContextWrapper : IMyDataContext
{
public void Dispose()
{
}
public ITable<MyTable> GetMyTable()
{
var table = MockRepository.GenerateMock<ITable<MyTable>>();
//todo: code to return something so that the linq query fired on this table works
return table;
}
}
如果我正确理解您的问题,您可能想要生成IMyDataContext的模拟,而不是仅为测试做自己的实现。
在IMyDataContext模拟中,您可以像这样设置期望:
var dataContext = MockRepository.GenerateMock<IMyDataContext>();
var table = MockRepository.GenerateMock<ITable<MyTable>>();
dataContext.Expect(x => x.GetMyTable()).Return(table);
您也可以在表模拟上设置期望。或者,您也可以创建一个实现ITable接口的类的新实例,并用内存中的测试数据填充该实例。
我不知道linq-to-sql,我不知道ITable
。但是当我看到这个界面时,我真的怀疑你是否应该嘲笑它。这太复杂了。模拟它可能会导致编写整个数据库模拟器,这显然没有意义。单元测试应该尽可能的简单。
我建议使用实现ITable
的真实类。明确一点:不要编写自己的实现。
ITable
包含3个不同的接口:IEnumerable
, IQueryable
和ITable
中的其他东西。由于您所做的只是查询表,因此您可以使用IQueryable
并将其传递给mock/存根:
IQueryable<MyTable> testTable = new[]{new MyTable{…}, new MyTable{…}, …, new MyTable{…}}.AsQueryable();
myMock.Stub(x => x.GetMyTable()).Return(testTable);
每个new MyTable{…}
代表表中的一行。
如果/一旦你需要完整的ITable
接口,例如,因为你想从表中添加或删除行,你将想要创建自己的抽象类TestableTable,你可以扩展List
(这样它已经实现了IEnumerable
)并提供所有IQueryable
方法:
public SomeType SomeMethodFromIQueryable(…)
{
return this.AsQueryable().SomeMthodFromIQueryable(…);
}
现在只剩下ITable
中的其他东西了。这些都很容易转化为List
-方法,除了.Commit()
,你可以保留抽象,然后可以存根它和.AssertWasCalled(…)
它。
var myMock = MockRepository.GenerateStub<TestableTable<MyTable>>(){ new MyTable{…}, …};
…
myMock.AssertWasCalled(x => x.Commit());
希望对你有帮助。
注:我是在为同样的问题寻找标准解决方案时遇到你的问题的。没有找到任何东西,所以我实现了这个
这是使用NSubstitute的,但您可以将其改编为Rhino。
[TestMethod]
public void Your_Test_Method()
{
var jobs = new List<JobInstance> { new JobInstance { JobInstanceId = 123 } };
ITable<JobInstance> jobsTable = FakeTable(jobs);
this.wfDataContext.JobInstances.Returns(jobsTable);
...
}
/// <summary>Sets up ITable substitution</summary>
/// <typeparam name="T">Type of ITable set</typeparam>
/// <param name="data">Data to work with</param>
/// <returns>Substituted ITable set</returns>
private static ITable<T> FakeTable<T>(List<T> data)
where T : class
{
IQueryable<T> dataQueryable = data.AsQueryable();
var fakeTable = Substitute.For<ITable<T>>();
fakeTable.Provider.Returns(dataQueryable.Provider);
fakeTable.Expression.Returns(dataQueryable.Expression);
fakeTable.ElementType.Returns(dataQueryable.ElementType);
fakeTable.GetEnumerator().Returns(dataQueryable.GetEnumerator());
return fakeTable;
}