Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing 对LINQ2SQL存储库进行单元测试_Unit Testing_Tdd_Mocking_Moq_Mstest - Fatal编程技术网

Unit testing 对LINQ2SQL存储库进行单元测试

Unit testing 对LINQ2SQL存储库进行单元测试,unit-testing,tdd,mocking,moq,mstest,Unit Testing,Tdd,Mocking,Moq,Mstest,我正在进行MsTest和Moq的第一步,希望对Linq2SQL存储库类进行单元测试。问题是我不希望单元测试永久性地修改我的开发数据库 对于这种情况,哪种方法是最好的 让每个测试在我的真实开发数据库上运行,但确保每个测试在运行之后都会进行清理 为单元测试创建我的开发数据库和dbml的副本,并改用该上下文,以便在每次测试运行之前清除整个数据库 找到一些精心设计的模仿Datacontext的方法(请记住,我是一个彻头彻尾的moqnoob) 完全不同的东西?也许在每次测试运行之前都能为我自动设置数据库

我正在进行MsTest和Moq的第一步,希望对Linq2SQL存储库类进行单元测试。问题是我不希望单元测试永久性地修改我的开发数据库

对于这种情况,哪种方法是最好的

  • 让每个测试在我的真实开发数据库上运行,但确保每个测试在运行之后都会进行清理
  • 为单元测试创建我的开发数据库和dbml的副本,并改用该上下文,以便在每次测试运行之前清除整个数据库
  • 找到一些精心设计的模仿Datacontext的方法(请记住,我是一个彻头彻尾的moqnoob)
  • 完全不同的东西?也许在每次测试运行之前都能为我自动设置数据库

Edit:我刚刚了解到MBUnit有一个回滚属性,可以反转测试用例运行的任何数据库操作。我不是特别喜欢MSTest,所以这可能是我问题的一个简单答案吗?

我使用一些包装器类+一个基于的假实现来模拟/伪造数据库。请注意,我最终在伪数据上下文包装器中实现了SubmitChanges逻辑,以测试实体的部分类实现中的验证逻辑。我认为这确实是唯一与托克利的实施有实质性区别的棘手部分

我将在下面包括我的FakeDataContextWrapper实现:

public class FakeDataContextWrapper : IDataContextWrapper
{

    public DataContext Context
    {
        get { return null; }
    }

    private List<object> Added = new List<object>();
    private List<object> Deleted = new List<object>();

    private readonly IFakeDatabase mockDatabase;

    public FakeDataContextWrapper( IFakeDatabase database )
    {
        mockDatabase = database;
    }

    protected List<T> InternalTable<T>() where T : class
    {
        return (List<T>)mockDatabase.Tables[typeof( T )];
    }

    #region IDataContextWrapper Members

    public virtual IQueryable<T> Table<T>() where T : class
    {
        return mockDatabase.GetTable<T>();
    }

    public virtual ITable Table( Type type )
    {
        return new FakeTable( mockDatabase.Tables[type], type );
    }

    public virtual void DeleteAllOnSubmit<T>( IEnumerable<T> entities ) where T : class
    {
        foreach (var entity in entities)
        {
            DeleteOnSubmit( entity );
        }
    }

    public virtual void DeleteOnSubmit<T>( T entity ) where T : class
    {
        this.Deleted.Add( entity );
    }

    public virtual void InsertAllOnSubmit<T>( IEnumerable<T> entities ) where T : class
    {
        foreach (var entity in entities)
        {
            InsertOnSubmit( entity );
        }
    }

    public virtual void InsertOnSubmit<T>( T entity ) where T : class
    {
        this.Added.Add( entity );
    }

    public virtual void SubmitChanges()
    {
        this.SubmitChanges( ConflictMode.FailOnFirstConflict );
    }

    public virtual void SubmitChanges( ConflictMode failureMode )
    {
        try
        {
            foreach (object obj in this.Added)
            {
                MethodInfo validator = obj.GetType().GetMethod( "OnValidate", BindingFlags.Instance | BindingFlags.NonPublic );
                if (validator != null)
                {

                    validator.Invoke( obj, new object[] { ChangeAction.Insert } );
                }
                this.mockDatabase.Tables[obj.GetType()].Add( obj );
            }

            this.Added.Clear();

            foreach (object obj in this.Deleted)
            {
                MethodInfo validator = obj.GetType().GetMethod( "OnValidate", BindingFlags.Instance | BindingFlags.NonPublic );
                if (validator != null)
                {
                    validator.Invoke( obj, new object[] { ChangeAction.Delete } );
                }
                this.mockDatabase.Tables[obj.GetType()].Remove( obj );
            }

            this.Deleted.Clear();

            foreach (KeyValuePair<Type, IList> tablePair in this.mockDatabase.Tables)
            {
                MethodInfo validator = tablePair.Key.GetMethod( "OnValidate", BindingFlags.Instance | BindingFlags.NonPublic );
                if (validator != null)
                {
                    foreach (object obj in tablePair.Value)
                    {
                        validator.Invoke( obj, new object[] { ChangeAction.Update } );
                    }
                }
            }
        }
        catch (TargetInvocationException e)
        {
            throw e.InnerException;
        }
    }

    public void Dispose() { }

    #endregion
}
公共类FakeDataContextWrapper:IDataContextWrapper
{
公共数据上下文
{
获取{return null;}
}
添加的私有列表=新列表();
已删除私有列表=新列表();
私有只读数据库;
公共FakeDataContextWrapper(IFakeDatabase数据库)
{
mockDatabase=数据库;
}
受保护的列表InternalTable(),其中T:class
{
返回(列表)mockDatabase.Tables[typeof(T)];
}
#区域IDataContextWrapper成员
公共虚拟IQueryable表(),其中T:class
{
返回mockDatabase.GetTable();
}
公共虚拟ITable表(类型)
{
返回新的FakeTable(mockDatabase.Tables[type],type);
}
公共虚拟void DeleteAllOnSubmit(IEnumerable实体),其中T:class
{
foreach(实体中的var实体)
{
删除提交(实体);
}
}
公共虚拟void DeleteOnSubmit(T实体),其中T:class
{
本.删除.添加(实体);
}
公共虚拟void InsertAllOnSubmit(IEnumerable实体),其中T:class
{
foreach(实体中的var实体)
{
InsertOnSubmit(实体);
}
}
公共虚拟void InsertOnSubmit(T实体),其中T:class
{
本.新增.新增(实体);
}
公共虚拟无效提交更改()
{
this.SubmitChanges(ConflictMode.FailOnFirstConflict);
}
公共虚拟无效提交更改(冲突模式故障模式)
{
尝试
{
foreach(此中的对象obj.Added)
{
MethodInfo validator=obj.GetType().GetMethod(“OnValidate”,BindingFlags.Instance | BindingFlags.NonPublic);
if(验证器!=null)
{
Invoke(obj,新对象[]{ChangeAction.Insert});
}
this.mockDatabase.Tables[obj.GetType()].Add(obj);
}
this.Added.Clear();
foreach(此.Deleted中的对象obj)
{
MethodInfo validator=obj.GetType().GetMethod(“OnValidate”,BindingFlags.Instance | BindingFlags.NonPublic);
if(验证器!=null)
{
Invoke(obj,新对象[]{ChangeAction.Delete});
}
this.mockDatabase.Tables[obj.GetType()].Remove(obj);
}
this.Deleted.Clear();
foreach(此.mockDatabase.Tables中的KeyValuePair表对)
{
MethodInfo validator=tablePair.Key.GetMethod(“OnValidate”,BindingFlags.Instance | BindingFlags.NonPublic);
if(验证器!=null)
{
foreach(tablePair.Value中的对象对象)
{
Invoke(obj,新对象[]{ChangeAction.Update});
}
}
}
}
捕获(目标)
{
抛出e.InnerException;
}
}
public void Dispose(){}
#端区
}

我对MBUnit进行了一些研究,并了解到,对于大多数测试用例,您可以通过使用MBUnit的[ROLLBACK]属性而不模拟datacontext

不幸的是,在某些情况下,属性会产生奇怪的副作用,例如从数据库加载linq实体,更改一个属性(不提交更改),然后再次加载相同的实体。通常这会导致数据库上没有更新查询,但在测试方法中,似乎只要我更改linq实体属性,就会立即执行更新


这不是一个完美的解决方案,但我认为我会使用[ROLLBACK]属性,因为它对我来说比较省力,而且效果也很好。

我也有类似的需求——对Linq to Sql类进行单元测试,所以我创建了一小组类,将模拟datacontext、ITables和IQueryables放入查询中


我将代码放在一篇博客文章“”中。它使用Moq,可以在不访问数据库的情况下为您正在进行的测试提供足够的功能。

是的,我也是这么做的。并创建了针对数据库运行的集成测试。Linq到Sql在这方面非常好——您可以直接从数据上下文中创建数据库。