Sunday, April 26, 2009

Adventures in mocking and faking the LINQ to SQL data context

Andrew Tokeley has an excellent blog post on mocking the LINQ to SQL data context, as implemented by his friend Stuart Clark. I've made heavy use of the code he posted in developing my own data context wrapper. In the process of testing my ASP.NET MVC application, though, I've found that a few enhancements can make this concept even more effective.

Faking Data

Clark's DataContextWrapper makes it possible to mock the LINQ to SQL data context. This is a tremendous advantage when developing unit tests that make use of the data context. Andrew's implementation is, however, more of a fake implementation rather than a mock. A fake implementation implements the same interface, but uses a simpler mechanism to accomplish the same tasks. A mock implementation, on the other hand, does not attempt to actually perform the same actions, but simply pretends to. Another difference is that the mock implementation typically tracks calls to its methods so that they can be verified, whereas a fake implementation is typically a simple stand-in for the actual implementation.

I decided early on, though, that I wanted to fake the data in addition to mocking the data context so this was actually ideal for me. Because so much of my application interacts with the database, I wanted to be able to use a DataContextWrapper that acted as much like the real database as possible. My feeling is that using a fake database would make it conceptually easier to write tests as if I were directly interacting with the database without having to consider all of the interactions that would go on under the hood. I suspect that there are many who would disagree with me, but I find that I work better with this model. One advantage is that I can simply reuse the fake data over and over again, yet when necessary I can still mock the DataContextWrapper, for example when I need one of its methods to throw an exception. When faking data I found that the fake implementation of the DataContextWrapper needed a few tweaks to make it really usable.

Inserting Data

The fake DataContextWrapper, as implemented by Stuart, adds the entity in the InsertOnSubmit method. However, the real LINQ To SQL data context doesn't insert the data into the database until SubmitChanges is called. Since I wanted my fake context to work as much like the real context as possible, I decided to implement a mechanism to keep track of the inserted data until SubmitChanges is called rather than add the entity to the fake table implementation during InsertOnSubmit. Likewise, it doesn't make much sense to use a relational database unless your data is related. Nearly all applications have entities that are related to each other. LINQ to SQL implements this using EntityRef (for one-to-one relationships) and EntitySets (for one-to-many relationships). The natural way to add related data in LINQ to SQL is to add it to the EntityRef or EntitySet representing the association.

   var context = new DataContextWrapper<MyDataContext>();
var masterEntity = new MasterEntity { ... };
masterEntity.RelatedEntities.Add( new RelatedEntity { ... } );
context.InsertOnSubmit( masterEntity );
context.SubmitChanges();



Without some special consideration, though, a mock DataContextWrapper doesn't support adding new related entities this way. I found myself writing code like this, instead, to pass my tests.

   var context = new DataContextWrapper<MyDataContext>();
var masterEntity = new MasterEntity { ... };
var relatedEntity = new RelatedEntity
{
MasterEntity = masterEntity,
...
}
masterEntity.RelatedEntities.Add( relatedEntity );
context.InsertOnSubmit( masterEntity );
context.InsertOnSubmit( relatedEntity );
context.SubmitChanges();

Note the difference between an actual context and the wrapper's implementation of InsertOnSubmit. I highly prefer the wrapper's implementation.



Clearly, to get the code I wanted, I needed to make my fake DataContextWrapper be able to detect when a newly inserted entity has related data and insert it as well. This would enable me to pass my tests without having to write extra code for the fake implementation. In order to do this I search the related entities for each of the entities stored in the fake tables for entities that are not in the appropriate table. These entities get added to the set of entities that need to be inserted into the fake implementation during the insert phase of SubmitChanges.

To do this I need a few helper methods for my FakeDataContextWrapper. These methods will iterate through an object's referenced objects and, if they aren't already in the fake data, schedule them to be added during the insert phase of SubmitChanges.


private void AddReferencedObjects( object entity )
{
foreach (var set in GetEntitySets( entity ))
{
foreach (var item in set)
{
if (!this.mockDatabase.Tables[item.GetType()].Contains( item ))
{
this.Added.Add( item );
}
}
}
foreach (var reference in GetEntityRefs( entity ))
{
if (!this.mockDatabase.Tables[reference.GetType()].Contains( reference ))
{
this.Added.Add( reference );
}
}
}

private IEnumerable<IEnumerable> GetEntitySets( object entity )
{
foreach (var property in entity.GetType().GetProperties())
{
if (property.PropertyType.Name.Contains( "EntitySet" ))
{
var value = property.GetValue( entity, null );
yield return value as IEnumerable;
}
}
}

private IEnumerable<object> GetEntityRefs( object entity )
{
foreach (var property in entity.GetType().GetProperties())
{
if (property.PropertyType.Name.Contains( "EntityRef" ))
{
yield return property.GetValue( entity, null );
}
}
}

Then we add a few lines of code to our SubmitChanges implementation to take care of actually updating the fake data when entities are added/updated/deleted. Notice how we make sure that all of the new objects to be inserted get added before the insert phase. The phases run, in order, insert, delete, update -- though one could probably switch the first two. As of yet, though, we don't have any need to address updates.


var directlyAdded = new List<object>( this.Added );
foreach (var obj in directlyAdded)
{
AddReferencedObjects( obj );
}

foreach (var list in this.mockDatabase.Tables.Values)
{
foreach (var obj in list)
{
AddReferencedObjects( obj );
}
}

foreach (var obj in this.Added)
{
this.mockDatabase.Tables[obj.GetType()].Add( obj );
}

this.Added.Clear();

foreach (var obj in this.Deleted)
{
this.mockDatabase.Tables[obj.GetType()].Remove( obj );
}

this.Deleted.Clear();
Validation

I decided to use Scott Guthrie's validation techniques on LINQ to SQL entities. To this end, I have a IValidatedEntity interface that my entities implement that defines a GetRuleViolations() method where my business rules are validated. In addition, I implement the OnValidate partial method, which calls GetRuleViolations to ensure that my entities are valid prior to saving them to the database. Unfortunately Andrew's mock context doesn't address the validation requirements. I decided to implement validation in the SubmitChanges method so that my fake DataContextWrapper would also perform validation just like the real context.

One issue that I ran into, however, is that the real data context tracks the changes that are made to existing entities so that it knows which ones need to be updated. It only updates those entities that have changed. Rather than add this complexity to the fake implementation, I decided instead to simply validate all entities as if they were being updated during the update phase of SubmitChanges. This incurs a little extra processing overhead for each unit test that touches code that does a SubmitChanges but the advantage is that the fake implementation is simpler.

I use reflection to find the OnValidate method for each entity and invoke it with the proper ChangeAction. Finally, in order to get the actual exception, instead of the exception thrown by the reflection calls, I wrap the entire SubmitChanges code in a try-catch block and throw the InnerException on errors.


public virtual void SubmitChanges( ConflictMode failureMode )
{
try
{
var directlyAdded = new List<object>( this.Added );
foreach (var obj in directlyAdded)
{
AddReferencedObjects( obj );
}

foreach (var list in this.mockDatabase.Tables.Values)
{
foreach (var obj in list)
{
AddReferencedObjects( obj );
}
}

foreach (var obj in this.Added)
{
MethodInfo validator = obj.GetType().GetMethod( "OnValidate",
BindingFlags.Instance | BindingFlags.NonPublic );
if (validator != null)
{
validator.Invoke( obj, new object[] { ChangeAction.Insert } );
}
this.mockDatabase.Tables[obj.GetType()].Add( obj );
}

this.Added.Clear();

foreach (var obj in this.Deleted)
{
MethodInfo validator = obj.GetType().GetMethod( "OnValidate",
BindingFlags.Instance | BindingFlags.NonPublic );
if (validator != null)
{
validator.Invoke( obj, new object[] { ChangeAction.Delete } );
}
this.mockDatabase.Tables[obj.GetType()].Remove( obj );
}

this.Deleted.Clear();

foreach (KeyValuePair<Type, IList> tablePair in this.mockDatabase.Tables)
{
MethodInfo validator = tablePair.Key.GetMethod( "OnValidate",
BindingFlags.Instance | BindingFlags.NonPublic );
if (validator != null)
{
foreach (var obj in tablePair.Value)
{
validator.Invoke( obj, new object[] { ChangeAction.Update } );
}
}
}
}
catch (TargetInvocationException e)
{
throw e.InnerException;
}
}

I've made a few other tweaks to the entire set of classes that support mocking the data context. I'll write about those later when I tackle automating auditing for LINQ to SQL.

Thursday, April 23, 2009

jQuery Cycle: Adding player controls to a slide show

I'm working on a web site for a local student organization, Dance Marathon, to help them track people who participant or donate to their fundraising activities. On the front page of the web site I have a slideshow that rotates through a series of photos from previous events. I'm using the jQuery Cycle plugin for this. I wanted to add some player controls so that the end user can control the operation of the slideshow.

The site uses the FamFamFam Silk icon set by Mark James, so I decided to use the control icons for my player controls. These icons are released and used under a Creative Commons Attribution 3.0 license. I highly recommend these icons.

I wanted to support the full range of player controls: Goto First Slide, Previous Slide, Stop Show, Play Show, Next Slide, and Goto Last Slide. Fortunately, the Cycle plugin allows me to pause and resume the slide show as well as choose a specific slide to show. Turns out that this is really all that is necessary to support those features. I also appreciate that the Silk icons include both blue and gray versions of the control icons. This allows me to give the user some visual feedback on which control is currently selected. By default the play control will be the active control on page load.

The Cycle plugin is very easy to set up. Simply create a DIV and assign it a class (or id). I used:


<div class="pics"><div>


Next generate the set of images that you want to include in your slideshow. I'm using ASP.NET MVC so I pass down an IEnumerable<ImageDescriptor>, where ImageDescriptor is a class containing Url and AltText properties for the images to include. This enumeration is produced in my controller by reading a specific subdirectory of my images directory.

public ActionResult Index()
{
ViewData["Title"] = "Home Page";
ViewData["Message"] = this.LocalStrings.WelcomeMessage;

List images = new List();
string homeImagePath = Server.MapPath( "~/Content/images/home-images" );
foreach (string imageFile in Directory.GetFiles( homeImagePath ))
{
string fileName = Path.GetFileName( imageFile );
string url = Url.Content( "~/Content/images/home-images/" + fileName );
images.Add( new ImageDescriptor { Url = url } );
}
return View( images );
}


Because my images are of different sizes, I decided to place the controls above the slideshow. The cycle plugin, by default, reserves enough space, by setting fixed width/height on the DIV, to show all of the slides. Since some of my images are oriented vertically and others horizontally, placing the controls below the slideshow would make it appear too far below the horizontal images. I'd prefer it below, so I'm still working on making this work for both orientations. If you have any ideas, let me know.

First, the view includes the player controls DIV so that they appear above the slideshow.

<div id="controls" class="hidden">
<img id="startButton"
src='<%= Url.Content( "~/Content/images/icons/control_start.png" ) %>'
alt="Beginning"
title="Beginning" />
<img id="prevButton"
src='<%= Url.Content( "~/Content/images/icons/control_rewind.png" ) %>'
alt="Previous"
title="Previous" />
<img id="stopButton"
src='<%= Url.Content( "~/Content/images/icons/control_stop.png" ) %>'
alt="Stop"
title="Stop" />
<img id="playButton" src='<%= Url.Content( "~/Content/images/icons/control_play_blue.png" ) %>'
alt="Play"
title="Play" />
<img id="nextButton"
src='<%= Url.Content( "~/Content/images/icons/control_fastforward.png" ) %>'
alt="Next"
title="Next" />
<img id="endButton"
src='<%= Url.Content( "~/Content/images/icons/control_end.png" ) %>'
alt="End"
title="End" />
</div>


Next I include the code to generate the gallery. This has been simplified from the actual code. Note that I make all but the first image hidden, as well as the player controls, when the page first loads in case Javascript is not enabled. This helps the page to downgrade gracefully if the user won't be able to use the gallery. Note that Image is my own HtmlHelper extension, although, I think that there is a similar one in MvcFutures. ParameterDictionary is also my own class, but it functions similar to RouteValueDictionary so you could use that instead.

<div class='pics'>
<% var klass = "";
foreach (ImageDescriptor image in Model)
{
var htmlOptions = image.HtmlOptions ?? new ParameterDictionary();
var url = Url.Content( image.Url );
%>
<%= Html.Image( url,
image.AltText,
htmlOptions.Merge( new { @class = klass } ))%>
<%
klass = "hidden";
}
%>
</div>


Finally, we come to the magic that makes it all work -- the Javascript code. We need to include jQuery and the Cycle plugin. These go in the header for the view. Note That I'm also using an HtmlHelper extension here to generate the script tags.

<%= Html.Javascript( Url.Content( "~/Scripts/jquery-1.3.2.min.js" ) ) %>
<%= Html.Javascript( Url.Content( "~/Scripts/cycle/jquery.cycle.all.min.js" ) ) %>


Remember that we want the controls positioned over the center of whatever image is displayed. The gallery div will be sized to fit the largest image, but the images may be of different sizes. I played with a number of different options, but finally decided to simply set the left margin of the player controls based on the image size and the size of the controls themselves. This is handled dynamically using the following function. This function will be set as the before callback on the Cycle plugin.

function positionControls( curr, next, options, forward )
{
if (controlWidth == 0)
{
$('#controls > img').each( function() {
controlWidth = controlWidth + $(this).width();
});
}
var nextWidth = $(next).width();
var leftMargin = (nextWidth - controlWidth) / 2;
if (leftMargin < 0) leftMargin = 0;
$('#controls').css( 'marginLeft', leftMargin + 'px' );
}


Also, remember that we want to have the clicked control highlighted as a visual indicator to the user. The easiest way to do this is to have the click handler for each of the controls set the image urls based on which control is clicked using the following function.

function iconSelected(img)
{
$('#controls > img').each( function() {
this.src = this.src.replace( /_blue/, '' );
});
img.src = img.src.replace( /.png$/,'_blue.png' );
}


Now to tie it all together, we remove the hidden class from all the elements, set up the Cycle plugin, and install the click handlers for the controls. Note that the Cycle plugin has native support for Previous and Next slide options, though it doesn't pause the show after advancing. We'll need to set up the other behaviors, however, and add some additional behavior to the Previous and Next controls.

I'm going to use the default fade effect for the Cycle plugin and set it up to use random display of the images. I'll precalculate the position of the last image in the set to use for the Goto Last Slide control. All controls, except, Play, will pause the slideshow after performing their respective behavior. Play simply resumes the show when clicked.

    var controlWidth = 0;
var lastImage = 0;
$(document).ready( function() {
$('#controls').removeClass('hidden');

var gallery = $('.pics');
gallery.cycle( { fx: 'fade',
random: true,
prev: '#prevButton',
next: '#nextButton',
before: positionControls } );

gallery.children('img').removeClass('hidden');

lastImage = gallery.children('img').size() - 1;
if (lastImage < 0) lastImage = 0;

$('#stopButton').click( function() {
gallery.cycle('pause');
iconSelected(this);
});
$('#playButton').click( function() {
gallery.cycle('resume',true);
iconSelected(this);
});
$('#prevButton,#nextButton').click( function() {
gallery.cycle('pause');
iconSelected(this);
});
$('#startButton').click( function() {
gallery.cycle('pause');
gallery.cycle(0);
iconSelected(this);
});
$('#endButton').click( function() {
gallery.cycle('pause');
gallery.cycle(lastImage);
iconSelected(this);
});
});


I'm pretty pleased with how this turned out, though I want to keep exploring options for position the player controls. It is disconcerting when they jump around as the image sizes change. Floating them left, though, looks odd to me. I'd love to hear your idea.