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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/wcf/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
C# 我可以模拟SqlConnection.BeginTransaction c吗?_C#_Unit Testing - Fatal编程技术网

C# 我可以模拟SqlConnection.BeginTransaction c吗?

C# 我可以模拟SqlConnection.BeginTransaction c吗?,c#,unit-testing,C#,Unit Testing,我必须处理一些无法更改的遗留代码。我必须编写实现一个名为“ITask”的接口的类,该接口有一个名为“RunTask”的方法,该方法接受一个名为Schedule的具体类型,例如 public void RunTask(Schedule thisSchedule) { //I have to do stuff with thisSchedule, no I can't fix that name... } 虽然遗留代码不使用单元测试,但我非常希望在工作中使用它,但问题是“这个时间表

我必须处理一些无法更改的遗留代码。我必须编写实现一个名为“ITask”的接口的类,该接口有一个名为“RunTask”的方法,该方法接受一个名为Schedule的具体类型,例如

 public void RunTask(Schedule thisSchedule)
 {
     //I have to do stuff with thisSchedule, no I can't fix that name...
 }
虽然遗留代码不使用单元测试,但我非常希望在工作中使用它,但问题是“这个时间表”。我已经制作了一个假的Schedule版本,它是由Schedule派生的,我正试图控制Schedule中所有方法的工作方式(这可能是一件傻事)。到目前为止,我已经成功地利用了惊人数量的虚拟方法或使用反射,但我已经击中了我的第一个显示停止

我无法连接到真实的数据库,我有一个经常被调用的方法

public void BeginTransaction()
{
    MyTransaction = MyConnection.BeginTransaction(IsolationLevel.RepeatableRead);
}

internal SqlConnection MyConnection = new System.Data.SqlClient.SqlConnection();
这会引发一个异常,因为连接已关闭。理想情况下,我希望能够设置一个标志,说明调用了该方法,然后吞下该异常,但我很乐意忽略该异常

任何让我通过这个方法调用的事情,无论多么糟糕,都是可以接受的答案。要么失败,要么失败

我不允许在visual studio enterprise中使用付费服务,如typeMock或Shimmers

编辑

请记住我不能改变课程安排,只要事情能这么简单就好了

ITask接口

    interface ITask
    {
        void RunTask(Schedule thisSchedule);
    }
事实上,您正在类中“更新”SQL连接,这使得很难反转控件并使其更易于单元测试

另一种选择是连接到虚拟数据库并使用该连接。虚拟数据库将有足够的数据来允许测试作为集成测试进行

该遗留代码应被视为您无法控制的第三方代码。您不应该浪费时间测试您无法控制的第三部分代码。通常的做法是将第三方代码封装在您所控制和使用的抽象背后。遗留代码表明,由于设计不善,它目前面临的困难正在被利用


考虑到当前的限制,没有什么其他的事情可以做。

当涉及到单元测试时,任何外部依赖都需要隔离。不同的框架有不同的隔离外部依赖性的方法,以及它们可以隔离的限制

许多框架允许您创建接口的模拟,以便可以为处于受控状态的成员提供实现。这些框架中的很多都能很好地处理抽象类的抽象成员。但是,很少有框架支持在具体类(非抽象成员)上提供实现。据我所知,仅有两种可用的方法是:


这两个框架都利用.NET Profiler API拦截成员调用,并用您提供的代码替换它们。我不太熟悉TypeMock隔离器,但我非常熟悉Microsoft Fakes。Microsoft Fakes框架支持生成程序集(即System.Fakes.dll),该程序集包含允许您在成员上提供自己的实现的类。它支持针对接口和抽象类(相当于“mock”)创建存根,并针对具体和静态类创建垫片

如果选择使用Microsoft Fakes,则首先需要针对System.Data.dll生成一个伪程序集,该程序集位于
SqlConnection
所在的位置。这将生成
ShimSqlConnection
类,该类将包含允许您提供
SqlConnection
类成员的替代实现的成员。下面是一个如何做到这一点的示例:

[TestMethod]
public void SampleTest()
{
    using (ShimsContext.Create())
    {
        // Arrange
        SqlConnection.AllInstances.BeginTransactionIsolationLevel =
            (instance, iso) => { ... };

        // Act

        // Assert
    }
}
当然,不管是模拟、填隙、存根还是其他什么。。。您应该提供模拟所需行为的替代实现。由于您要替换该成员,因此您仍必须遵守其合同和预期行为;您不应该返回实际实现不会返回的值或引发异常。在我的公司,我们利用Microsoft Fakes模拟以下场景:

  • 当我们从
    流中读取时,如果该流关闭,会发生什么情况
  • 如果HttpWebRequest的用户/密码错误,会发生什么情况
  • 如果希望数据库返回两行(即控制
    SqlDataReader.Read()
    成员),会发生什么情况
在所有这些场景中,我们的目标是隔离这些成员的实现行为,并用在受控实验中实际发生的实现替换它们


更新:


我刚刚注意到原始海报上写着“我不允许在visual studio enterprise中使用typeMock或Shimmers等付费服务。”。如果这是一个限制,那么您必须找到另一个我不知道的工具,它使用.NET Profiler API或自己使用框架

我能得到的最接近的方法是创建一个名为IFake的接口,我将应用于FakeSchedule

public interface IFake
{
    Action GetFakeAction(Action action);
}

public class FakeSchedule : Schedule, IFake
{
    public bool TransactionStarted { get; set; }

    public Action GetFakeAction(Action action)
    {
        return () => TransactionStarted = true;
    }
}
然后我创建了以下扩展方法

    public static void Testable<T>(this T obj, Action action)
    {
        if(obj is IFake)
        {
            action = ((IFake)obj).GetFakeAction(action);
        }
        action();
    }
显然,这个解决方案远非理想,但它是有效的,如果我正在运行一个单元测试,那么开始事务的调用就偏离了真正的实现


我实现的版本将需要一些工作(我的GetFakeAction实现太愚蠢了),但这是我认为我将要得到的最接近的版本,我能看到的唯一其他解决方案是使用或遵循并连接到虚拟数据库。

您可以使用免费的Moq。还要研究如何使用接口。这将使单元测试这类东西更容易。在事务处理的情况下,也许您可以将IDbTransaction.Moq传递给win。当谈到.Net中的模拟时,它是一个神奇的子弹。请在一个可以用来重现问题的文档中提供更多细节。那么可能是一个解决方案/解决方案b
    public static void Testable<T>(this T obj, Action action)
    {
        if(obj is IFake)
        {
            action = ((IFake)obj).GetFakeAction(action);
        }
        action();
    }
    [TestMethod]
    public void BeginTransaction_WhenCalled_SetsTransactionStartedToTrue()
    {
        Schedule schedule = new FakeSchedule();
        var connection = new SqlConnection();

        schedule.Testable(schedule.BeginTransaction);

        Assert.IsTrue(((FakeSchedule)schedule).TransactionStarted); 
    }