C#单元测试中向模拟数据库添加数据的方法
这篇文章更像是讨论的开始,因为我对单元测试和TDD有些陌生 我目前正在为一个与多个数据库交互的.NET进程编写一些单元测试,并正在使用模拟数据库上下文,试图在测试中覆盖不同的边缘情况,验证程序本身的异常处理,等等。也就是说,我的一些单元测试使用有效数据,而其他的则不使用 我正在寻找在向模拟数据库上下文中添加有效/虚假数据时建议的最佳实践方面的反馈。我见过很多人这样做(例如,实现存储库模式、将模拟数据添加到.csv文件并使其成为项目的一部分,等等) 我目前正在考虑使用存储库模式将C#单元测试中向模拟数据库添加数据的方法,c#,unit-testing,mocking,tdd,C#,Unit Testing,Mocking,Tdd,这篇文章更像是讨论的开始,因为我对单元测试和TDD有些陌生 我目前正在为一个与多个数据库交互的.NET进程编写一些单元测试,并正在使用模拟数据库上下文,试图在测试中覆盖不同的边缘情况,验证程序本身的异常处理,等等。也就是说,我的一些单元测试使用有效数据,而其他的则不使用 我正在寻找在向模拟数据库上下文中添加有效/虚假数据时建议的最佳实践方面的反馈。我见过很多人这样做(例如,实现存储库模式、将模拟数据添加到.csv文件并使其成为项目的一部分,等等) 我目前正在考虑使用存储库模式将Survey对象添
Survey
对象添加到目标数据库中的Surveys
表中
首先,我有一个界面:
公共接口是surveyrepository
{
IQueryable SurveySeries{get;}
}
这是为单元测试所需的模拟假/有效数据存储库实现的
class FakeSurveyRepository:ISurveyRepository
{
私有静态IQueryable fakeSurveySeries=新列表{
新调查{id=1,SurveyName=“NotValid1”,SurveyData=“fake”},
新调查{id=2,SurveyName=“NotValid2”,SurveyData=“super fake”},
.........,
新调查{id=10,SurveyName=“NotValid10”,SurveyData=“the fakest”}
}.AsQueryable();
公共可查询调查
{
获取{return fakeSurveySeries;}
}
}
//RealSurveyRepository:ISurveyRepository与此类似,但数据“良好”
然后,通过向构造函数中的序列传递引用,我有一个类来为假/有效数据使用这些数据:
public class SurveySeriesProcessor
{
private ISurveyRepository surveyRepository;
public SurveySeriesProcessor( ISurveyRepository surveyRepository )
{
this.surveyRepository = surveyRepository;
}
public IQueryable<Survey> GetSurveys()
{
return surveyRepository.SurveySeries
}
}
public class SurveySeriesProcessor
{
私人调查报告;
公共调查报告处理人(ISURVEY报告调查报告)
{
this.surveyRepository=surveyRepository;
}
公共IQueryable GetSurveys()
{
返回调查repository.SurveySeries
}
}
然后可以在我的测试中使用这些对象,例如:
[TestClass]
public class SurveyTests
{
[TestMethod]
WhenInvalidSurveysFound_SurveyCopierThrowsInvalidSurveyDataErrorForEach()
{
// create mocking DB context and add fake data
var contextFactory = new ContextFactory( ContextType.Mocking );
var surveySeriesProcessor = new SurveySeriesProcessor( new FakeSurveyRepository() );
foreach(Survey surveyRecord in surveySeriesProcessor.GetSurveys() )
{
contextFactory.TargetDBContext.Surveys.AddObject( surveyRecord );
}
// instantiate object being tested and run it against fake test data
var testSurveyCopier = new SurveyCopier( contextFactory );
testSurveyCopier.Start();
// test behavior
List<ErrorMessage> errors = testSurveyCopier.ErrorMessages;
errors.Count.ShouldEqual( surveySeriesProcessor.GetSurveys().Count );
foreach(ErrorMessage errMsg in errors)
{
errMsg.ErrorCode.ShouldEqual(ErrorMessage.ErrorMessageCode.InvalidSurveyData);
}
}
}
[TestClass]
公共类调查
{
[测试方法]
当发现无效调查时\u SurveyCopierThrowsInvalidSurveyDataErrorForEach()
{
//创建模拟数据库上下文并添加假数据
var contextFactory=新的contextFactory(ContextType.Mocking);
var surveySeriesProcessor=新的surveySeriesProcessor(新的FakeSurveyRepository());
foreach(surveySeriesProcessor.GetSurveys()中的SurveySeriesRecord)
{
contextFactory.TargetDBContext.Surveys.AddObject(surveyRecord);
}
//实例化正在测试的对象,并针对假测试数据运行它
var testSurveyCopier=新的SurveyCopier(contextFactory);
testSurveyCopier.Start();
//测试行为
列出错误=testSurveyCopier.ErrorMessages;
errors.Count.ShouldEqual(surveySeriesProcessor.GetSurveys().Count);
foreach(错误消息errMsg in errors)
{
errMsg.ErrorCode.ShouldEqual(ErrorMessage.ErrorMessageCode.InvalidSurveyData);
}
}
}
注意:我意识到,在提供的示例代码中,我不一定需要让实现ISurveyRepository
的类将序列作为IQueryable
返回(它们很可能是List
)。但是,我将在将来扩展接口和这些类的功能,以根据添加到LINQ查询中的某些条件过滤出假/有效序列,这就是为什么我让存储库实现IQueryable
。这是模拟代码,旨在传达我所想的基本原则
考虑到所有这些,我想问的是:
这是一次公开的讨论。请记住,这是我写的第一套单元测试(不过,我已经阅读了大量关于这个主题的文献)。我认为你的思路很好 就个人而言,在同样的情况下,如果我处理的是存储库样式的模式
public interface IRepository<T>
{
IEnumerable<T> GetAll();
}
public class PonyRepository : IRepository<Pony>
{
IEnumerable<Pony> GetAll();
}
公共接口IRepository
{
IEnumerable GetAll();
}
公共类PonyRepository:IRepository
{
IEnumerable GetAll();
}
为了实际提供所需的数据,我通常创建一个TestObjects或TestFakes类来按需提供所需的数据
public class FakeStuff
{
public static IEnumerable<Pony> JustSomeGenericPonies(int numberOfPonies)
{
// return just some basic list
return new List<Pony>{new Pony{Colour = "Brown", Awesomeness = AwesomenessLevel.Max}};
// or could equally just go bananas in here and do stuff like...
var lOfP = new List<Pony>();
for(int i = 0; i < numberOfPonies; i++)
{
var p = new Pony();
if(i % 2 == 0)
{
p.Colour = "Gray";
}
else
{
p.Colour = "Orange";
}
lOfP.Add(p);
}
return lOfP;
}
}
public-class-FakeStuff
{
公共静态IEnumerable JustSomeGenericPonies(int numberOfPonies)
{
//只返回一些基本列表
返回新列表{new Pony{color=“Brown”,Awesomeness=AwesomenessLevel.Max};
//或者也可以在这里发疯,做一些像。。。
var lOfP=新列表();
for(int i=0;i
并以此进行测试:
[Test]
public void Hello_I_Want_to_test_ponies()
{
Mock<IRepository<Pony> _mockPonyRepo = new Mock<IRepository<Pony>>();
_mockPonyRepo.SetUp(m => m.GetAll()).Returns(FakeStuff.JustSomeGenericPonies(50));
// Do things that test using the repository
}
[测试]
公共无效你好我想测试小马()
{
Mock m.GetAll()).Returns(FakeStuff.JustSomeGenericPonies(50));
//使用存储库进行测试
}
所以,通过保留假数据,这提供了假数据的可重用性
[Test]
public void Hello_I_Want_to_test_ponies()
{
Mock<IRepository<Pony> _mockPonyRepo = new Mock<IRepository<Pony>>();
_mockPonyRepo.SetUp(m => m.GetAll()).Returns(FakeStuff.JustSomeGenericPonies(50));
// Do things that test using the repository
}
public class FakePonyRepositoryThatOnlyReturnsBrownPonies : IRepository<Pony>
{
private List<Pony> _verySpecificAndNotReusableListOfOnlyBrownPonies = new List....
public IEnumerable<Pony> GetAll()
{
return _verySpecificAndNotReusableListOfOnlyBrownPonies;
}
}
public class FakePonyRepositoryThatThrowsExceptionFromGetAll : IRepository<Pony>
{
public IEnumerable<Pony> GetAll()
{
throw new OmgNoPoniesException();
}
}