Trying to Mock the Unmockable
(or, DataRowView How I Hate Thee)
Recently I had to enhance an ASP.NET 2.0 GridView on a Web Form. Basically, the idea is that, if a certain field in the grid was false, I would strike-through all the text in that row. It seems simple enough: just use the RowDataBound event of the GridView control and apply the formating. Wanting to make this as testable as possible, I thought that perhaps some sort of decorator class would do the trick, something like:
myGridView.RowDataBound +=delegate(object sender, GridRowEventArgs rowEventArgs){InventoryPartVendorGridRowDecorator gridRowDecorator =new InventoryPartVendorGridRowDecorator(rowEventArgs.Row);
gridRowDecorator.Decorate();};
Then, using the dynamic duo of NUnit and Rhino.Mocks, I would be able to test my decorator class independent of my web application. I thought I could mock out the GridViewRow and a DataRowView, and be on my merry way. Life would be good. And it would have been, until the wretched DataRowView entered into the picture.
DataRowView sits pretty high up on the inheritance tree, doesn't have any interfaces that are meaning full in this context, and it's constructors are internal. So, it proved to be pretty difficult to mock.
To explain what I did, I will first show you my unit test:
[Test]public void Decorate_InactiveVendor_TextDecorationLineThrough(){MockRepository mockery = new MockRepository();
GridViewRow mockGridViewRow = mockery.CreateMock<GridViewRow>(1, 1, DataControlRowType.DataRow, DataControlRowState.Normal);Hashtable dataRowViewDataItem = new Hashtable(1);
dataRowViewDataItem.Add("Active", false);using (mockery.Record())
{Expect.Call(mockGridViewRow.RowType).Return(DataControlRowType.DataRow).Repeat.Once();Expect.Call(mockGridViewRow.DataItem).Return(dataRowViewDataItem).Repeat.Once();mockGridViewRow.Style.Add(HtmlTextWriterStyle.TextDecoration, "line-through");
}using (mockery.Playback())
{InventoryPartVendorGridRowDecorator gridRowDecorator =new InventoryPartVendorGridRowDecorator(mockGridViewRow);
gridRowDecorator.Decorate();}}
Notice what I did; I created a Hashtable and have my mockGridViewRow.DataItem return that. In otherwords, I'm not mocking DataRowView at all, I just cut it out of the picture. Then inside inside my InventoryPartVendorGridRowDecorator class I had to put this method:
private bool IsVendorActive(){object val;
object dataItem = _row.DataItem;
// HACK [TO070508@1149] Because we can't mock DataRowView, we do this.
if (dataItem is DataRowView){val = ((DataRowView) dataItem)[ActiveColumnName];}else if (dataItem is IDictionary){val = ((IDictionary) dataItem)[ActiveColumnName];}else
{val = null;
}if (val == null){throw new NullReferenceException("Could not find the field 'Active'.");}return TypeHelper.ConvertToBoolean(val, true);}
As you can see, what I ended up doing was sticking in a little "adapter" into my decorator. Not pretty, but it works.
What would others do in this situation?