1

There are some varbinary table columns in my database that i need to test for equality with a byte array, all happening through Entity Framework and Linq-to-Entities. For Linq-to-Objects, this is achieved very easily with the SequenceEquals extension method, however EF doesn't support it unfortunately, therefore i rely on the == operator which is correctly translated into a SQL query.

E.g.

byte[] deviceId = GetDeviceId();

Device device = _deviceRepository.QueryAll() //backed by a DbContext, returns IQueryable<Device>
.Include(d => d.RelatedEntity1)
.Include(d => d.RelatedEntity2)
.Include(d => d.RelatedEntityEtc)
.FirstOrDefault(d => d.DeviceUniqueIdentifier == deviceId && ...);

LoginUserOnDevice(device);

The problem is, though, when unit testing the piece of code involving the Linq-to-Entities query, with a fake _deviceRepository, for whom i use an ordinary List<T> transformed through an .AsQueryable() into an IQueryable<T>, the equality operator is applied with its original semantics, i.e. reference equality, so the comparison obviously fails. Of course, this doesn't happen strictly for unit testing, but in any situation where the linq query is not executed anymore against a SQL data source.

What would be the optimal approach here? Preferably without changing the query code, so it runs correctly both when querying against a real database (using EF 6) and while unit testing. Materializing the entire collection into memory by calling .ToList() right after the Includes or anything along these lines is out of the question in my scenario.

Gabriel S.
  • 1,347
  • 11
  • 31

1 Answers1

2

You can and should avoid this whole thing. Instead of performing a query on your fake repository you should let it simply return the data you need for your test; otherwise you're basically attempting to test whether or not EF does its work (even though here you mock it out).

It's one of the main reasons to replace that repository with a fake: you want to take away the need to call the database for data and instead you return some pre-defined dataset with which you can test the working of your unit.

If you really want to test your LINQ query then you're first of all looking at an integration test: you're testing how your system interacts with an external one. At this point it should be obvious that you won't need the fake repository either, since that wouldn't be testing the interaction with your (dev) database.

It is indeed a hindrance to work with legacy code that obstructs proper testing. As you noticed you can't do it anymore the way you'd want to (and how you'd normally do it: by returning mock data from your repository using the same LINQ query).

If it would be possible to make the query behave the way you want to (which I am very unsure of) it would be too much of a hassle. What I would suggest you to do instead is to mock the call to your repository instead of stubbing the repository itself. The chain of responsibility has been broken by putting the query outside of the repository so changing the repository won't solve your problem.

I'm not entirely sure if this will behave the way I want it to so play around with it a little but I believe that this should do it (using Moq):

var device = new Device(id: 5, name: "washingmachine", price: 5000);
var repository = new Mock <IDeviceRepository>();
repository.Setup(x => x.QueryAll())
          .Returns(device);

If you now execute your test with the LINQ query, it will disregard anything it does and instead return your predefined device.

Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
  • That's a good advice, if this were an earlier period in the lifetime of my project i might have changed that repository to return strictly `IEnumerable` instead of `IQueryable`, with appropriate methods, but now it's a bit late. This is the price i pay for highly coupling the domain model to the entity model. Also, i am not actually testing the EF query, i actually want to stub out the device repository so that the query itself won't get in the way, then i use the retrieved (fake) device to assert things further on. – Gabriel S. May 26 '14 at 16:52
  • Could you edit your post with a more elaborate code sample? It's unclear where this LINQ query is exactly located. – Jeroen Vannevel May 26 '14 at 16:58
  • I added a bit more context to the query but i don't think any more than that would still be relevant. The method encapsulating this code is a public method that will also be the entry point of the unit test. – Gabriel S. May 26 '14 at 17:02
  • @GabrielS.: Added something that I believe should address the problem. – Jeroen Vannevel May 26 '14 at 17:12
  • Many thanks, but unfortunately the `Include()` calls make this a lot more complicated - i have to mock each `Include()` call, one at a time, otherwise Moq complains. The result, as you can imagine, would be ugly beyond any hope of readability. But it may be indeed a solution, i'll try to see how feasible it is. – Gabriel S. May 26 '14 at 17:15
  • Is it an option to refactor the code instead to something more testable? Otherwise I'm out of ideas, it's patchwork from henceforth. – Jeroen Vannevel May 26 '14 at 17:17
  • It never hurts to try. I'll try refactoring those `Includes` somewhere inside the abstract repository using the advice given in [this](http://stackoverflow.com/a/5376637/475673) thread, maybe it will work. – Gabriel S. May 27 '14 at 07:01