Simulating SQL Server (Part 3)

If you haven’t done so so already, start with –

Simulating SQL Server (Part 1)
Simulating SQL Server (Part 2)

In the first two posts, I introduced the SqlTester library, which can be downloaded at the bottom of this post.  I also showed a series of classes that implemented IDataRecord and IDataReade to allow the creation of ‘fake’ database connections when testing code in unit tests.  For anyone who has tried messing around with compact databases (and the like) in their test projects, the resulting library can be a real time saver.

I also showed how the library could be used to quickly generate random data, using the same techniques first discussed in Generating Randomness.  That means we have the tools to create real test data and create lots of random data.  There is still one thing missing in our arsenal if we want to achieve a reasonable level of code coverage with our data layer tests.  We need to be able to simulate exceptions.

In the first post in the series, I introduced the IObjectRecord interface without comment, whilst introducing the ObjectRecord  class that implemented the interface.  It may have seemed strange that I placed an interface there, why not just use the class itself?  In the second post, I also introduced the IObjectSet , but this time, I showed two implementations – ObjectSet  and RandomSet .  The beauty of using the interfaces is it makes it easy to extend the base library yourself with further implementations.

It should come as no surprise then that I’ve included another implementation of IObjectRecord  as well – ExceptionRecord .  As the name suggests this record will throw an exception whenever it is accessed.  Unlike our other records, though, we don’t need to supply a record set definition.  Just like with a normal error we won’t be able to access any columns or data without this error being thrown anyway, so knowing the record set definition is irrelevant.

With this one class we can now ‘poison’ any record set, and make it fail exactly when we want to, and in the way we want to.

Of course, we still have a small problem here, what if we want to simulate an actual SQL Server error?  Perhaps your code (correctly) only intercepts exceptions descending from SqlException.  Unfortunately, there is no public constructor for creating a SqlException, after all, you shouldn’t normally be able to create one yourself as it’s reserved for errors coming from SQL.

Thankfully, using the power of reflection, pretty much anything is possible, so I created some prototype classes to solve this particular conundrum for you easily.  As a technique you can use this for any such similar problem, but bare in mind people make classes private for a reason, so use with caution!

So how do these work?

Well basically all three prototype classes ( SqlErrorPrototype , SqlErrorCollectionPrototype  and SqlExceptionPrototype ) correspond to a framework class (SqlError, SqlErrorCollection and SqlException respectively).  And all three have implicit conversion operators defined to allow conversion between the prototype class and it’s corresponding framework class.  For example, SqlErrorPrototype  contains the following code –

Because we can implicitly convert to and from the prototype, the prototype can be used anywhere the real class is needed.

If you look at the methods above, you’ll also see a reference to a public readonly  field called SqlError .  This field is actually created the moment you create an instance of the object –

Looking a the highlighted lines you can see that this calls an internal method (in fact it’s a private static readonly  field of type Func<> ) that creates our actual SqlError.  So how is this magic function created?  Well, it’s created in a static constructor, and here comes a great example of where statics are useful.  Because it’s in the static constructor it will be called once (and only once) whenever the class is first accessed – this is a cheap effective (and thread-safe) way to solve lazy initialisation.

Using the exact same techniques we create the other two prototype classes, which of course are in the code you can download.  I shall leave an explanation of how the code above works until a later post (when I introduce a generic function for creating Action<> s and Func<> s) but for now be assured that calling the resultant function is as quick as calling the constructor directly (and way quicker than the appalling Activator.CreateInstance).

Finally, the ObjectReader  has a few public methods that aren’t exposed via the IDataReader interface (or the collection interface).  In the end, the ObjectReader  is a simple collection that keeps track of a single enumeration through it, unlike normal collections, where you can request multiple enumerators and enumerate them independently.  As any seasoned .NET developer will tell you, you can’t modify a collection whilst it is being enumerated.  So once you start using an ObjectReader  you can’t manipulate its contents.  To be accurate you can add/remove records to a record set other than the current one, but you can’t add/remove record sets.

To see where the ObjectReader  got up to you can use the CurrentSet  and Current  properties, which return the current set and the current record (which will be in the current set) respectively.

Unlike a real data reader, though, you can also use the Reset  method on ObjectReader  to reset the data reader back to the start (whilst keeping the data intact) so you can re-run a test (for example).

In the end, there’s a lot of functionality in the library and although I’ve covered some of the basics in these posts you can’t beat diving into the code and playing with it, so I recommend you view the code on GitHub or use the download link below to see the complete project.  All I ask is that you leave a comment or feedback, and even some suggestions if you’d like improvements.  The code is free to use in open source or commercial projects, just acknowledge it’s source.

I hope you find a use in your own tests!

Comments 5

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.