在.Net中对数据库进行存根/模拟

时间:2011-04-05 09:59:21

标签: c# database testing mocking

我有一个web服务,基本上只执行一些存储过程,转换数据并将其发送到浏览器。没有花哨的ORM映射器或类似的东西。为了能够在不访问数据库的情况下编写测试,我已经完成了以下操作:

  • 我已将对DB的所有调用提取到一个类中。这些方法只返回DataSet和DataTable对象。
  • 为每个方法执行了一个示例调用,并将DataSet / DataTable序列化为磁盘。
  • 提取展示所有可用方法的界面。
  • 实现了一个伪数据库类,它只加载序列化数据并将其返回。

现在我已经序列化了样本结果,我可以用我的项目检查,我可以在我的测试中使用假数据库。

这对我来说效果很好。是否有一些框架可以更轻松地创建和加载示例数据?我当前的项目很小,但我会在较大的项目中使用相同的模式。

更新

显然所有答案都没有错,但忽略了这一点。我知道单元测试的基础知识。但我的代码正在使用DataTables,所以我不得不以某种方式伪造我的DataTables。从头开始构建DataTable并不是一件容易的事,它会使我的测试膨胀并降低可读性。就我而言,手动生成有用的样本数据是非常不可能的。

因此,我对示例数据库执行了一些示例调用以获取一些DataTable。我已将这些表序列化到磁盘并使用序列化版本在测试时创建我的假DataTables。这样,测试就独立于数据库。

有关如何构造代码的不同选项,使表的反序列化更容易。但这些是实施细节,此时不需要讨论。我的问题如下:

管理示例调用和(de)序列化表是一项繁琐的工作。我正在寻找一些工具来使这更容易。

6 个答案:

答案 0 :(得分:29)

通过阅读您所做的其他答案和各种评论,您似乎想要一种更简单的方法来生成用于集成测试的大型填充数据集,而不会影响数据库。

NBuilder是一个很棒的开源库,我已经成功地创建了大量的测试数据。只需将NBuilder,一些基本的POCO对象类和一些反射结合起来 - 您就可以在很短的时间内轻松地将很多巨大的数据表组合成数据集:

public class Person
{
    public string First { get; set; }
    public string Last { get; set; }
    public DateTime Birthday { get; set; }
}

private DataTable GenerateDataTable<T>(int rows)
{
    var datatable = new DataTable(typeof(T).Name);
    typeof(T).GetProperties().ToList().ForEach(
        x => datatable.Columns.Add(x.Name));
    Builder<T>.CreateListOfSize(rows).Build()
        .ToList().ForEach(
            x => datatable.LoadDataRow(x.GetType().GetProperties().Select(
                y => y.GetValue(x, null)).ToArray(), true));
    return datatable;
}

var dataset = new DataSet();
dataset.Tables.AddRange(new[]{
        GenerateDataTable<Person>(50),
        GenerateDataTable<Dog>(100)});

答案 1 :(得分:6)

要对转换进行单元测试,您根本不需要模拟数据库。我怀疑你已经将转换与数据库调用紧密耦合在一起。你想要做的是将你所有的转换逻辑提取到它自己的类中,如下所示:

public static Transformations
{
    public static DataSet TransformationA(DataSet dataSet)
    {
        //transformation logic here
    }

    public static DataSet TransformationB(DataSet dataSet)
    {
        //transformation logic here
    }
}

使用此功能,您可以通过传入数据集来单元测试转换逻辑,然后断言返回的数据集应用了正确的转换。这将阻止您必须实现另一个数据存储(您的'假'数据库)仅用于测试目的。

希望这会有所帮助

答案 2 :(得分:2)

您可以使用Rhinomocks模拟DataAccess类并返回虚假数据表。因此,您可以测试使用此DataTable的代码。

var mockedDatatable= GetMockdt();

var mocks = new MockRepository();
var dal = mocks.StrictMock<DataAccess>();

using (mocks.Record())
{
  Expect.Call(dal.GetDataTableFromDatabase("", null)).Return(mockedDatatable).IgnoreArguments();
}

using (mocks.Playback())
{
  new SomeClass(dal);
}

更新mockdt消息

private static DataTable GetMockdt()
{
  var dt = new DataTable();

  dt.Columns.Add("pageHeader");
  dt.Columns.Add("templatename");
  dt.Columns.Add("pageText");
  dt.Columns.Add("pageTitleBar");
  dt.Columns.Add("metaDescription");
  dt.Columns.Add("pageStartCode");
  dt.Columns.Add("pageEndCode");
  dt.Columns.Add("templateStartCode");
  dt.Columns.Add("templateEndCode");
  dt.Columns.Add("Author");
  dt.Columns.Add("version_date");
  dt.Columns.Add("pageurl");
  dt.Columns.Add("type");
  dt.Columns.Add("isparent");
  dt.Columns.Add("pagename");
  dt.Columns.Add("parentname");
  dt.Columns.Add("url");

  var mockRow = dt.NewRow();

  mockRow["pageHeader"] = "homepage";
  mockRow["pageText"] = "<p>home</p>";
  mockRow["templatename"] = "home";
  mockRow["pageTitleBar"] = "homepages";
  mockRow["metaDescription"] = "homepages";
  mockRow["pageStartCode"] = "homepages";
  mockRow["pageEndCode"] = "homepages";
  mockRow["templateStartCode"] = "homepages";
  mockRow["templateEndCode"] = "homepages";
  mockRow["Author"] = "someone";
  mockRow["version_date"] = "";
  mockRow["pageurl"] = "home";
  mockRow["type"] = "internal";
  mockRow["isparent"] = "true";
  mockRow["pagename"] = "homepage";
  mockRow["parentname"] = "root";
  mockRow["url"] = "homepage";

  dt.Rows.Add(mockRow);

  return dt;
}

答案 3 :(得分:0)

根据我的经验,使用Fluent NHibernate进行端到端测试相当容易。没有理由不使用这样的轻量级层,因为它对你来说非常重要。

Persistence specification testing

答案 4 :(得分:0)

由于您要求将数据存储为DataTables,并且需要数据库中的原始数据,因此没有任何工具可以执行您想要的操作。工具的手动部分是指示什么(即存储中的数据与代码中的数据表示)。您已完成此部分,而不是自动化的部分。

答案 5 :(得分:0)

查看http://nbuilder.org/

“它是什么?

通过流畅,可扩展的界面,NBuilder允许您快速创建测试数据,自动将值分配给属于内置.NET数据类型(例如整数和字符串)类型的属性和公共字段。 NBuilder允许您使用lambda表达式覆盖您感兴趣的属性。“