Following up on my Simple.Data Faking experiment

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:

dynamic fake = FakeResult.For.Table.Method().Returns();

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:

public class SomeRepository
{
  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)

[Fact]
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

dynamic fakeResult1 = new FakeResult();
dynamic fakeResult2 = 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.

dynamic fakeResult1 = new FakeResult();
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:

dynamic fakeResult = FakeResult.For.Table.All().Returns(values);
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> = fakeResult.RetrieveCalls();
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:

var fakeResult = new FakeResult();
//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

Categories: