33

I have a webservice which basically just executes some stored procedures, transforms the data and sends it to the browser. No fancy ORM mapper or something like that involved. To be able to write test without accessing the database, I have done the following:

  • I have extracted all calls to the DB into one class. The methods return just the DataSet and DataTable objects.
  • Executed a sample call for each method and serialized the DataSet/DataTable to disk.
  • Extracted an interface exposing all available methods.
  • Implemented a fake database class which just loads the serialized data and returns it.

Now I have serialized sample results which I can check in with my project, and I can use the fake database in my tests.

This works quite well for me. Is there some framework which makes creating and loading the sample data easier? My current project is small, but I would use the same schema in larger projects.

Update:

Obviously all answers are not wrong, but miss the point. I'm aware of the basics of unit testing. But my code is working with DataTables, so I would have to somehow fake my DataTables. Building a DataTable from scratch is not an easy task, and it would bloat my tests and reduce readability. In my case, it would be quite impossible to generate useful sample data by hand.

Therefore, I executed some sample calls against a sample database to get some DataTables. I have serialized these tables to disk and use the serialized versions to create my fake DataTables when testing. That way the tests are independent of the database.

There are different options regarding how to structure the code, to make deserialization of the tables easier. But those are implementation details which don't need a discussion at this point. My problem is the following:

Managing the sample calls and (de)serializing the tables is tedious work. I was looking for some tools to make this easier.

Ryan Gates
  • 4,501
  • 6
  • 50
  • 90
Achim
  • 15,415
  • 15
  • 80
  • 144
  • So what exactly are your tests testing? that the fake db class can return fake data? or are you testing the transformation? – Roger Johansson Apr 05 '11 at 10:04
  • I'm testing the transformation. This part of the tests assumes that the database is fine and I would like to test everything that is happening after getting the DataSet/DataTable from the database. – Achim Apr 05 '11 at 10:08
  • 4
    If you are testing just the transformation, ideally you would isolate the code for that, and write unittests strictly on that code. – Chris O Apr 05 '11 at 12:07
  • 1
    Agree with Chris, separate out the transformation and use DI to feed it the data that should be transformed.. then you could throw away your fake dataclasses – Roger Johansson Apr 05 '11 at 12:44
  • 1
    I agree with Chris O on using DI and testing Xforms in vacuum. I would like to add that DB testing can also be done in isolation. See Fluent NHibernate for an example of how to do that with fairly limited amount of coding. – GregC Apr 15 '11 at 23:37

6 Answers6

32

From reading the other answers and various comments you've made, it seems you want an easier way to generate large populated datasets for integration testing that doesn't hit the database.

NBuilder is a great open-source library that I've successfully used to create large amounts of test data. Simply combine NBuilder, a few basic POCO object classes, and some reflection - you'll have plenty of huge datatables you can easily combine into datasets in no time:

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)});
Mathieu VIALES
  • 4,526
  • 3
  • 31
  • 48
Bermo
  • 4,921
  • 1
  • 28
  • 31
6

To unit test the transformation you really shouldn't need to mock the database at all. I suspect that you've tightly coupled the transformations with you database calls. What you want to do here is extract all your transformation logic into a class of it's own like the following:

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

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

With this you can unit test only the transformations logic by passing in a dataset and then asserting the dataset returned has the correct transformations applied to it. This will prevent you from having to implement another data store (your 'fake' database) for testing purposes only.

Hopefully this helps

Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
stuartf
  • 648
  • 4
  • 8
  • 1
    See my update of the question. The main problem is not the transformation, but the creation of the sample DataSets/Tables. – Achim Apr 12 '11 at 06:54
  • I think stuartf is entirely correct. The only reason I can see so far that the problem is 'but the creation of the sample DataSets/Tables.' – Dave Apr 19 '11 at 08:16
2

You could mock your DataAccess class with Rhinomocks and return a fake datatable. So you can test the code that uses this 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);
}

UPDATE the mockdt message

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;
}
Dan Atkinson
  • 11,391
  • 14
  • 81
  • 114
Ivo
  • 3,406
  • 4
  • 33
  • 56
  • Please see the update of my questions and the other answers: I know how to mock/stub an object. I'm interested in how you would implement GetMockdt() – Achim Apr 12 '11 at 14:10
  • If you read my update again, you will see that your solution is exactly what I don't want to do. Creation of sample data would be a lot of work in my case. My question targets exactly towards a simple solution for what you propose. – Achim Apr 12 '11 at 14:25
  • Aha I see, you want a tool to generate sample data – Ivo Apr 12 '11 at 14:42
  • Check out this post on so, maybe it can help you http://stackoverflow.com/questions/591892/tools-for-generating-mock-data – Ivo Apr 12 '11 at 14:46
  • @Ivo: do you think that the provided code is refactoring-friendly? – GregC Apr 15 '11 at 23:52
  • Well the datatable comes from that database, so its hard to test it anyway, because things can change in the database that you cant with a unit test. – Ivo Apr 16 '11 at 17:17
0

In my experience, it has been fairly easy to make end-to-end testing work with Fluent NHibernate. There's no excuse not to use such a lightweight layer when it does so much for ya.

Persistence specification testing

GregC
  • 7,737
  • 2
  • 53
  • 67
0

There are not any tools for doing what you want because of your requirements that your data be stored as DataTables, and you need original data from the database. The manual part of tools is wiring up what points to what (i.e. your data in storage to your data representation in code). You have already done this part, and it is not what is automated.

Charles Lambert
  • 5,042
  • 26
  • 47
0

Check out https://github.com/nbuilder/nbuilder

"What is it?

Through a fluent, extensible interface, NBuilder allows you to rapidly create test data, automatically assigning values to properties and public fields that are of type of the built in .NET data types (e.g. ints and strings). NBuilder allows you to override for properties you are interested in using lambda expressions."

Buckaroo Banzai
  • 103
  • 1
  • 1
  • 8
Ivo
  • 3,406
  • 4
  • 33
  • 56