我必须使用一些我无法更改的遗留代码。我必须编写实现一个名为“ITask”的接口(interface)的类,该接口(interface)有一个名为“RunTask”的方法,该方法接受名为 Schedule 的具体类型,例如;
public void RunTask(Schedule thisSchedule)
{
//I have to do stuff with thisSchedule, no I can't fix that name...
}
虽然遗留代码不使用单元测试,但我非常想将它用于我的工作,但问题是“thisSchedule”。我制作了一个从它派生的 Schedule 的假版本,并试图控制其中的所有方法如何运行(这可能是一个傻瓜的差事)。到目前为止,我已经通过利用数量惊人的虚拟方法或使用反射取得了成功,但我已经遇到了第一个阻碍
我无法连接到真实的数据库,并且我有一个经常被调用的方法;
public void BeginTransaction()
{
MyTransaction = MyConnection.BeginTransaction(IsolationLevel.RepeatableRead);
}
internal SqlConnection MyConnection = new System.Data.SqlClient.SqlConnection();
这会引发异常,因为连接已关闭,理想情况下,我希望能够设置一个标志,表明该方法已被调用,然后只是吞下该异常,但我很乐意简单地忽略该异常。
任何事情,无论多么令人讨厌,只要能让我通过这个方法调用,都是可以接受的答案。要么失败,要么失败。
我不允许在 Visual Studio Enterprise 中使用 typeMock 或 shim 等付费服务。
编辑
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Data;
using System.Data.SqlClient;
namespace PredictionServicesTests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void BeginTransaction_WhenCalled_SetsTransactionStartedToTrue()
{
Schedule schedule = new FakeSchedule();
schedule.BeginTransaction(); //It would be enough to simply get past this line to the Assert
Assert.IsTrue(((FakeSchedule)schedule).TransactionStarted); //This would be nice, but isn't vital
}
class Schedule
{
private SqlTransaction MyTransaction;
internal SqlConnection MyConnection = new System.Data.SqlClient.SqlConnection();
public void BeginTransaction()
{
MyTransaction = MyConnection.BeginTransaction(IsolationLevel.RepeatableRead);
}
}
class FakeSchedule : Schedule
{
public bool TransactionStarted { get; set; }
}
}
}
请记住,我无法更改 Schedule 类,但愿事情能这么简单!
ITask 接口(interface);
interface ITask
{
void RunTask(Schedule thisSchedule);
}
最佳答案
当涉及到单元测试时,任何外部依赖都需要隔离。不同的框架有不同的隔离外部依赖的方式,以及它们可以隔离的限制。
许多框架允许您创建接口(interface)的模拟,以便您可以为处于受控状态的成员提供实现。许多这样的框架也可以很好地与抽象类的抽象成员配合使用。然而,很少有框架支持在具体类(非抽象成员)上提供实现的能力。我知道的唯一两个可用的是:
这两个框架都利用 .NET Profiler API 来拦截成员调用,并用您提供的代码替换它们。我对 TypeMock Isolator 不太熟悉,但对 Microsoft Fakes 非常熟悉。 Microsoft Fakes 框架支持生成程序集(即 System.Fakes.dll),其中包含允许您在成员上提供自己的实现的类。它支持针对接口(interface)和抽象类(相当于“模拟”)创建 Stub,以及针对具体类和静态类创建 Shims。
如果您选择使用 Microsoft Fakes,您首先需要针对 System.Data.dll 生成一个伪造的程序集,这是 SqlConnection
所在的位置。这将生成 ShimSqlConnection
类,该类将包含允许您提供 SqlConnection
类成员的替代实现的成员。以下是如何做到这一点的示例:
[TestMethod]
public void SampleTest()
{
using (ShimsContext.Create())
{
// Arrange
SqlConnection.AllInstances.BeginTransactionIsolationLevel =
(instance, iso) => { ... };
// Act
// Assert
}
}
当然,无论是模拟、垫片、 stub ,还是无论什么......您都应该提供模拟您想要的行为的替代实现。由于您要替换 SqlConnection.BeginTransaction(IsolationLevel)
成员(member),您仍必须遵守其契约(Contract)和预期行为;您不应该返回实际实现不会执行的值或抛出异常。在我的公司,我们利用 Microsoft Fakes 来模拟以下场景:
- 如果我们读取时
Stream
被关闭,会发生什么? - 如果
HttpWebRequest
的用户/密码错误会发生什么? - 如果我们希望数据库返回两行(即控制
SqlDataReader.Read()
成员),会发生什么?
在所有这些场景中,我们的目标是隔离这些成员的已实现行为,并将其替换为在受控实验中实际发生的实现。
更新:
我刚刚注意到原始发帖者声明“我不允许在 Visual Studio Enterprise 中使用 typeMock 或 shim 等付费服务。”。如果这是一个限制,那么您将不得不找到另一个我不知道的使用 .NET Profiler API 的工具或自己使用该框架。 Profiling (Unmanaged API Reference)
.
关于c# - 我可以模拟 SqlConnection.BeginTransaction c# 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43350993/