I’ve been a huge fan of Rhino Mocks since I heard about it on a way-back-episode of Dot Net Rocks HanselMinutes. One of the more awesome features of Rhino Mocks is that it utilizes a Record/Playback semantic so that you can set up your mocks with actual calls to the mock objects, then play them back in your tests. This is great if you rely on intellisense when calling your object instances, or if you use Resharper or another code refactoring tool.

Recently, I trying to use Rhino Mocks with DataTables like so:

DataTable a = new DataTable();
DataTable b = new DataTable(); 

MockObject mock = mockRepository.CreateMock<MockObject>();

using (mockRepository.Record())
{
   Expect.Call(mock.CreateRecord("TableName", a, b).Return(1);
}
using (mockRepository.Playback())
{
  MyObject obj = new MyObject(mock);
  obj.Save();
}

When I ran the test, Rhino Mocks says that a the DataTable created during the obj.Save() call wasn’t the same as the one expected. This was correct as there were now two instances, and it seems that the default check Rhino Mocks performs is an Object.Equals(). Since I needed to do some more deep comparisons of the Expected and Actual DataTables, a fellow developer and I started a very simple DataTableConstraint:

    class DataTableConstraint : AbstractConstraint
    {
        private readonly DataTable _expected;
        private string _errorMessage;

        public DataTableConstraint(DataTable expected)
        {
            _expected = expected;
        }

        public override bool Eval(object obj)
        {
            DataTable actual = obj as DataTable;
            if (actual == null)
                return false;

            if (!CheckColumns(actual))
            {
                return false;
            }
            if (!CheckData(actual))
            {
                return false;
            }
            return true;
        }

        private bool CheckData(DataTable actual)
        {
            if (actual.Rows.Count == 0)
            {
                _errorMessage = "Actual table has no data to compare";
                return false;
            }
            try
            {
                for (int i=0; i < _expected.Rows.Count; i++)
                {
                    foreach (DataColumn column in _expected.Columns)
                    {
                        object expectedCell = _expected.Rows[i][column];
                        object actualCell = actual.Rows[i][column];

                        if (expectedCell != actualCell)
                        {
                            _errorMessage = string.Format("Expected {0} in Row ({1}), Column ({2}), but was {3}", expectedCell, i, column.ColumnName, actualCell);
                            return false;
                        }
                    }
                }
                return true;
            }
            catch (System.Exception e)
            {
                _errorMessage = e.Message;
                return false;
            }
        }

        private bool CheckColumns(DataTable actual)
        {
            foreach (DataColumn column in _expected.Columns)
            {
                if (!actual.Columns.Contains(column.ColumnName))
                {
                    _errorMessage = string.Format("Could not find column {0} in {1}", column, actual);
                    return false;
                }
            }
            return true;
        }

        public override string Message
        {
            get { return _errorMessage; }
        }
    }

To use this using the above sample code, do

Expect.Call(mock.CreateRecord(null, null, null).Constraints
(
  Is.Anything(),
  new DataTableConstraint(a),
  new DataTableConstraint(b)
);

Now, this is just a start, and will probably evolve more as I start to get more into verifying the data in two tables. Perhaps Oren will create a new Syntax Helper along the lines of Data.Equals(expectedDataTable) since using a new Constraint() call is a little awkward.

Advertisements