Unit testing dependent repository classes—difficulty mocking DataConnection.
See original GitHub issueI’m not sure if this is a library design issue, or if I’m just taking the wrong approach to testing. I’m new to this library, so go easy on me if I’m just overlooking something stupid.
I’m having a difficult time trying to find a practical way to unit test my repository classes that rely on this library. Ideally, I’d like to test the functionality of my classes themselves, mocking the behavior of the LinqToDb.Data.DataConnection dependency so I can isolate my own code.
However, I’m having a hard time doing that, and I can’t seem to much advice on how to proceed. I did find this blog post by @cskardon from 2012, which is a pretty low-effort way to mock data retrieval in the DataConnection by using a basic IQueryable implementation, but the moment my mocks attempt to call the Insert/Update/etc extension methods, things get weird.
Using the documentation as a guide, I have something along these lines for a data context class:
public class RateDataContext : LinqToDB.Data.DataConnection
{
public LinqToDBFacetsContext(IDataProvider provider, string connectionString)
: base (provider, connectionString) {}
public virtual ITable<Rate> Rates => GetTable<Rate>();
}
And the following repository that depends on it:
public class LinqToDbRateRepository : IRateRepository
{
private RateDataContext Context { get; }
public LinqToDbRateRepository (RateDataContext context)
{
Context = context;
}
public Rate GetRate(int rateId)
{
// ... some logic here about the ID entered
var rate = Context.Rates.SingleOrDefault(r => r.Key == rateId);
// With the MockTable, my tests get through this just fine.
// ... some transformation logic here
return rate;
}
public int CreateRate(Rate rate)
{
// ... some validation and transformation logic here.
// My attempts to mock (MockTable, Moq, etc) blow up with this extension method call:
var result = db.InsertWithIdentity(rate);
int newId = Convert.ToInt32(result);
// ... any internal tracking that needs to be done with the new key.
return newId;
}
}
I’ve tried using Moq for the data context class, but since the .InsertWithIdentity, .Insert, .Update methods are all extension methods, I can’t fake those as part of my mocks. Unless I’m missing something, it seems like I’d have to write a fair number of custom implementations to provide all the things those methods depend on in order to mimic the behavior.
Is there a better way to approach designing my classes to more effectively isolate my code for unit testing? Or is this library just not a good candidate for unit testing? What practices have you seen in the wild?
Issue Analytics
- State:
- Created 7 years ago
- Comments:9 (5 by maintainers)
My opinion - database code should be tested against database, because mock will just hide issues you have in query.
If you need to test database-agnostic logic - maybe you should separate your code to do something one and inject database-specific part as mockable dependency.
Actually several years ago I had similar idea to write tests with mocking linq2db using IEnumerable data, but I only implemented linq2db abstraction and never really mocked it in tests (still it helped with tests a lot).
It contains:
interface IQuerableExtensions
that duplicates linq2db extensions (only those I actually use):with implementation for linq2db:
Next you need to connect it to your DataConnection/mock provider
In the end I didn’t ever implemented in-memory db mock for this infrastructure, but actually this extra abstraction layer helped me a lot with tests:
So far I have found that only the four “transaction” methods (
BeginTransaction()
,BeginTransaction(IsolationLevel isolationLevel)
,CommitTransaction()
andRollbackTransaction()
) are preventing me from mocking the DataConnection class. My suggestion is, if you don’t want to add the transaction methods to theIDataContext
interface maybe you can create another interface that contains these four methods (call itITransaction
) and add that interface to the DataConnection class.This would give us the ability to test our repository classes without requiring us to run your Linq2Db code in our tests AND eliminate the need for an external database.