-
-
Save skoon/994671 to your computer and use it in GitHub Desktop.
using System; | |
using System.Collections.Generic; | |
namespace MostlyScottish { | |
//This is what I think we should move towards | |
public interface IRepository<T> { | |
void Save(T entity); | |
} | |
public class StuffRepository : IRepository<Stuff> { | |
public void Save(Stuff entity) { | |
//save the Stuff | |
} | |
} | |
public class Stuff { | |
//poco DB entity class | |
} | |
public class StuffDoer { | |
private readonly IRepository<Stuff> _StuffRepo; | |
public StuffDoer(IRepository<Stuff> stuffRepo) { | |
_StuffRepo = stuffRepo; | |
} | |
public void MethodUnderTest() { | |
// blah blah blah, stuff we can't mock because they are all private instance methods | |
IEnumerable<Stuff> stuffCollection = new List<Stuff>(); | |
foreach (var thing in stuffCollection) { | |
_StuffRepo.Save(thing); | |
//Now there is a direct link to the Save method on the StuffRepository. | |
} | |
} | |
} | |
} |
using System; | |
using System.Collections.Generic; | |
namespace NotScottish { | |
//http://www.youtube.com/watch?v=KrQxQPG8JdE&feature=related | |
//So this is what we have now. | |
public interface IRepository<T> { | |
void Save(T entity); | |
} | |
public interface IStuffRepository : IRepository<Stuff> { | |
} | |
public abstract class BaseRepository<T> : IRepository<T> { | |
public void Save(T entity) { | |
//method that accepts string,Func<ISession,T> | |
} | |
} | |
public class StuffRepository : BaseRepository<Stuff>, IStuffRepository { | |
//Trying to use NSubstitute to mock the "Save" method, but where is it really at runtime? | |
} | |
public class Stuff { | |
//poco DB entity class | |
} | |
public class StuffDoer { | |
private readonly IStuffRepository _StuffRepo; | |
public StuffDoer(IStuffRepository stuffRepo) { | |
_StuffRepo = stuffRepo; | |
} | |
public void MethodUnderTest() { | |
// blah blah blah, stuff we can't mock because they are all private instance methods | |
IEnumerable<Stuff> stuffCollection = new List<Stuff>(); | |
foreach (var thing in stuffCollection) { | |
_StuffRepo.Save(thing); | |
//Where is the Save method located at runtime and what can we really mock at this point? | |
//I think it's really located in BaseRepository BUT my magic mocker thinks that it is located on the instance of | |
//StuffRepository. So when it mocks "Save" on the StuffRepository, the mock method is located at one place on | |
//the heap, and the real Save method on the BaseRepository is located at another location on the heap. Both the | |
//StuffRepository and BaseRepository have their own MethodTables. | |
} | |
} | |
} | |
} |
Mostly scottish works fine until you need a FindStuffByFoo() method. This will only make sense on IStuffRepository and then once you start injecting that you are back to not-scottish
In fact, if you DON"T have any Stuff-specific methods on your stuff repository, I wouldn't even create one. Just use the base implementation (it would have to be non abstract).
Yeah, it really threw me for a loop today. We tried using NSubstitue, which uses syntax like:
var mock = Substitute.For();
which is where I think the real problem is. Interfaces don't really exist at runtime, they are just a compiler artifact and in this case the two instances, BaseRepository and StuffRepository, end up in two different spots on the heap and with two different method tables. So Substitute is putting some method named "Save" on the StuffRepository instance because it implements, through inheritance, the IRepository interface. But the real method is located on BaseRepository, which I can't really mock.
yeah, there are some Stuff specific methods on the real repo that make it necessary. But I think it can just implement IRepository. BaseRepository really only saving us from typing out the IRepository methods.
Oh in that case ignore my fork.
I'm surprised I haven't encountered this because it all looks pretty textbook. Maybe this is just an issue with your particular mock framework of choice?