C# 如何使用Autofixture创建和填充模拟类?
目前,我正在使用EF6在UnitOfWork中实现我的存储库。我还创建了一个内存中的模拟实现(MockUnitOfWork&MockRepository),以便在单元测试中使用它们,但现在我必须处理冗长的对象设置 这不是Autofixture设计的目的吗?我将如何获得一个MockUnitOfWork,以便在包含已填充的Foo和Barr存储库的测试中使用?我正在使用NSubstitute作为模拟框架 工夫C# 如何使用Autofixture创建和填充模拟类?,c#,autofixture,nsubstitute,C#,Autofixture,Nsubstitute,目前,我正在使用EF6在UnitOfWork中实现我的存储库。我还创建了一个内存中的模拟实现(MockUnitOfWork&MockRepository),以便在单元测试中使用它们,但现在我必须处理冗长的对象设置 这不是Autofixture设计的目的吗?我将如何获得一个MockUnitOfWork,以便在包含已填充的Foo和Barr存储库的测试中使用?我正在使用NSubstitute作为模拟框架 工夫 public interface IUnitOfWork { void Save()
public interface IUnitOfWork
{
void Save();
void Commit();
void Rollback();
IRepository<Foo> FooRepository { get; }
IRepository<Bar> BarRepository { get; }
}
公共接口IUnitOfWork
{
作废保存();
无效提交();
无效回滚();
i存储库{get;}
Irespository barrespository{get;}
}
间接的
public interface IRepository<TEntity> where TEntity : class
{
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null, string includeProperties = "");
IEnumerable<TEntity> Get(Expression<Func<TEntity, bool>> filter = null, Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null);
TEntity GetByID(object id);
void Insert(TEntity entity);
void Delete(object id);
void Delete(TEntity entityToDelete);
void Update(TEntity entityToUpdate);
}
公共接口假定,其中tenty:class
{
Func orderBy=null,字符串includeProperties=“”);
IEnumerable Get(表达式过滤器=null,Func orderBy=null);
TEntity GetByID(对象id);
无效插入(TEntity实体);
作废删除(对象id);
无效删除(TEntity entityToDelete);
无效更新(TEntity entityToUpdate);
}
是的,这正是它的设计目的。请参见下面的示例。我使用Mock而不是NSubstitute,因为我不熟悉NSubstitute。您只需通过另一个自定义,并在设置中使用NSubstitute语法
[SetUp]
public void SetUp()
{
// this will make AutoFixture create mocks automatically for all dependencies
_fixture = new Fixture()
.Customize(new AutoMoqCustomization());
// whenever AutoFixture needs IUnitOfWork it will use the same mock object
// (something like a singleton scope in IOC container)
_fixture.Freeze<Mock<IUnitOfWork>>();
// suppose YourSystemUnderTest takes IUnitOfWork as dependency,
// it'll get the one frozen the line above
_sut = _fixture.Create<YourSystemUnderTest>();
}
[Test]
public void SomeTest()
{
var id = _fixture.Create<object>(); // some random id
var fooObject = _fixture.Create<Foo>(); // the object repository should return for id
// setuping THE SAME mock object that wa passed to _sut in SetUp.
// _fixture.Freeze<Mock part is ESSENTIAL
// _fixture.Freeze<Mock<IUnitOfWork>>() returns the mock object, so whatever comes
// next is Mock specific and you'll have to use NSubstitute syntax instead
_fixture.Freeze<Mock<IUnitOfWork>>()
.Setup(uow => uow.FooRepository.GetById(id))
.Returns(fooObject);
// if this method will ask the unit of work for FooRepository.GetById(id)
// it will get fooObject.
var whatever = _sut.SomeMethod(id);
// do assertions
}
[设置]
公共作废设置()
{
//这将使AutoFixture自动为所有依赖项创建模拟
_夹具=新夹具()
.自定义(新的AutoMoqCustomization());
//每当AutoFixture需要IUnitOfWork时,它将使用相同的模拟对象
//(类似于IOC容器中的单例作用域)
_fixture.Freeze();
//假设您的SystemUndertest将IUnitOfWork作为依赖项,
//它会把上面的那条冻结
_sut=_fixture.Create();
}
[测试]
公共测试()
{
var id=_fixture.Create();//一些随机id
var fooObject=_fixture.Create();//对象存储库应返回id
//正在设置wa在设置中传递给_sut的同一模拟对象。
//\u fixture.Freeze您正在尝试在这里进行功能测试,因此最好有一个功能数据库
EF可以使用测试连接字符串在安装和拆卸方法中重新创建和销毁数据库。这将为您的测试提供一个真实的功能测试环境,以模拟真实环境进行操作
例:
您必须使用自动模拟扩展。您可以了解更多有关它的信息。如果您使用xUnit.net,您也可以查看。正如@NikosBaxevanis所说,将AutoFixture和NSubstitute绑定在一起的粘合库是AutoFixture.AutoNSubstitute。但是,它可能不会像您希望的那样做,因为它无法填充您的存储库.AutoFixture擅长在对象图中填充数据,但模拟不包含数据,它会公开行为,这是一件完全不同的事情。为了使模拟具有正确的行为,您必须对其进行配置。这与其说是AutoFixture问题,不如说是NSSubstitute问题,因此我添加了NSSubstitute标记。我的理解是我有两个选择。1)使用Nsub/moq类型的框架来模拟UOW的行为2)使用我的IUnitOfWork的一个具体的两倍,并用测试数据填充它。听起来正确吗?看看努力。走irepository+Mock的路线只是一个受伤的世界。我试过了,它做得不好。TBH,我同意frog,它不值得ef目前,我们正在使用事务在测试后恢复数据库。
[TestFixtureSetUp]
public static void SetupFixture() //create database
{
using (var context = new XEntities())
{
context.Setup();
}
}
[TestFixtureTearDown]
public void TearDown() //drop database
{
using (var context = new XEntities())
{
context.Database.Delete();
}
}
[SetUp]
public void Setup() //Clear entities before each test so they are independent
{
using (var context = new XEntities())
{
foreach (var tableRow in context.Table)
{
context.Table.Remove(tableRow);
}
context.SaveChanges();
}
}