Skip to content

Instantly share code, notes, and snippets.

@skoon
Created May 27, 2011 05:00
Show Gist options
  • Save skoon/994671 to your computer and use it in GitHub Desktop.
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.
}
}
}
}
@paulbatum
Copy link

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?

@paulbatum
Copy link

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

@paulbatum
Copy link

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).

@skoon
Copy link
Author

skoon commented May 27, 2011

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.

@skoon
Copy link
Author

skoon commented May 27, 2011

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.

@paulbatum
Copy link

Oh in that case ignore my fork.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment