Gabriel S.
Gabriel S.

Reputation: 1347

How can the equality operator for byte arrays be faked in Linq-to-Entities while unit testing?

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.

Upvotes: 1

Views: 536

Answers (1)

Jeroen Vannevel
Jeroen Vannevel

Reputation: 44448

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.

Upvotes: 2

Related Questions