Following up on my Simple.Data Faking experiment
Submitted by Vidar on
A couple of weeks ago I blogged about an idea I had for mocking (and / or stubbing) database results from Simple.Data.
Since then I've attempted to write out my ideas, and I've gotten the project into a state where it actually is usable for some cases.
In short this post will be explaining how to use Simple.Data.FakeResult.
I need to emphasise that this is still very much an experiment.
It's me exploring programming with dynamic objects in C# and api development.
Still, I would really appreciate some feedback (or even better criticism). Both on the general idea of mocking / stubbing the db this way, and on my concrete api and implementation.
The first stub
The main use of the Simple.Data.FakeResult framework will probaby be stubbing out some db code to test some surrounding logic.
A simple stub would be set up like this:
Other frameworks providing stubs require less setup, like the FakeItEasy: var stub = A.Fake
The reason we can't just do this to stub out the Simple.Data database is because there is no predefined Interface defining all he behaviours. It's all dynamic.
To be able to provide a stub we have to define the behaviours up front.
After this setup process we have an object that will be interchangeable with the dynamic database / datastrategy object returned from the Database.Openxxx() methods provided by the Simple.Data framework.
Please use this in a sentence
To give you an overview of where this fakeresult object fits in consider this code:
{
IDb _dbProvider;
public SomeRepository (IDb dbProvider)
{
_dbProvider = dbProvider;
}
public User FindById(Guid id)
{
return _dbProvider.Db.Users.FindById(id);
}
}
public class RealDbProvider : IDb
{
private string _connString = "SomeConnectionString";
public dynamic Db {get { return Database.OpenConnection(_connString); } }
}
public class InUse
{
public void UsingTheRepository()
{
var dbProvider = new RealDbPRovider();
var repository = new SomeRepository(dbProvider);
var aUserThatWillNotBeUsedForAnythingUseful = repository.FindById(1);
}
}
The IDb interface represents a factory for Simple.Data database connections.
And this databaseConnection is what the Simple.Data.FakeResult aims to provide stubbing and mocking for.
Admittedly there's not mutch to test here, but an imaginary test of this could be done like this:
(Using Xunit and FakeItEasy, my favourite general purpose stubbing and mocking framework)
public void This_test_is_really_redundant()
{
dynamic fake = FakeResult.For.Users.FindById().Returns(new User(1, "John Doe"));
var dbProvider = A.Fake<IDb>();
A.CallTo(dbProvider).WithReturnType<dynamic>.Returns((FakeResult) fake);
var repositoryToBeTested = new SomeRepository(dbProvider);
var user = repositoryToBeTested.FindById(1);
Assert.Equal(1, user.Id);
}
Step by step
Prerequisite: Som way of intercepting the Simple.Data database connection from the Database.Openxxx methods. (Like the Idb interface used above)
1. Create the FakeResult Object
This can be done by a std new FakeResult() call, or using the static factory property: FakeResult.For
Define the property and method chains that will be used during the test
It's important that the method count and names matches what will be called during the test.
fakeResult1.Table.Query().Where().Select().Take().Returns(values);
dynamic fakeResult2 = FakeResult.For.Table.FIndById().Returns(value);
The .Returns() method call finalizes the definition of the call chain and provides the return value it will yield during the test. The .Returns() is not a part of the methods that can be called on the fakeResult object during the test-
Inject the FakeResult into your object
This really depends on what other frameworks you use in your tests, here is once again an example using FakeItEasy:
var fakeDbProvider = A.Fake<IDbProbivider>();
A.CallTo(fakeDbProvider).WithReturnType<dynamic>().Returns((fakeResult) fakeDbProvider);
var objectToBeTested = new ClassToBeTested(fakeDbProvider);
Not that the FakeResult object needs to be cast explicitly in the FakeItEasy .Returns() method.
What about mocking
So far I have not created a proper mocking api, but it is possible to extract information from the calls to the fakeResult object after it's been used:
IEnumerable
This is still rather rough, but the string part of the Tuple is the methodname / path (ie: "Db.Table.Method.Method2"), The object array is an array of all argument values used in the method call.
To assert on how the database was called:
//Define behaviour, and perform actions
//Throws an exception if method was never called
fakeResult.AssertThat().Table.Method().WasCalled();
//Throws an exception if method was called, but non of the arguments
//have the given value
fakeResult.AssertThat().Table.Method().WasCalled().ContainingArguments(value);
//Throws an exception if method was called, but the arguments given is not
//Exactly the same and in the same order as expected
fakeResult.Assert.That().Table.Method().WasCalled().WithArguments(value, value2);
Links
- Original blogpost describing the idea
- Github project, now allso containing some example code
- Simple.Data github project
- FakeItEasy Google code project page
- Xunit Codeplec project page