1

Given the following example code:

Table<Person> tableToQuery = MethodThatGetsReferenceToPersonTable();
string searchType = "NonUser";
IQueryable<Person> queryResults = tableToQuery.Where(p =>
    (p.IsMale == false) && // exclude male people
    type.Equals("User", StringComparison.OrdinalIgnoreCase)
        ? p.User != null : p.User == null);

I am new to L2S, but do have some of experience with EntityFramework. I would not expect the above query to work correctly in an ORM like EF because of the type.Equals that gets invoked in the predicate's ternary expression. I would instead expect EF to throw an exception since it cannot convert that part of the predicate into a (SQL) store expression.

With L2S, it seems that the .Where is returning data, but it is not excluding items where p.Male == true when type == "NonUser. I have already fixed the code above to pull the type.Equals ternary out of the predicate and return correct results, and am now trying to write a test to assert the correct results. The problem I am running into is that the L2S code is actually behind an IRepository<Person> interface.

So the actual code looks more like this:

string searchType = "NonUser";
ICriteria<Person> query = new Query<Person>(p =>
  (p.IsMale == false) && // exclude male people
  type.Equals("User", StringComparison.OrdinalIgnoreCase)
      ? p.User != null : p.User == null);
IQueryable<Person> = _peopleRepository.FindByQuery(query)

...and the _peopleRepository implementation just passes the query.Predicate as an argument to the L2S Table<Person>.Where.

Questions:

  1. Is it correct that the L2S Table<Person>.Where is not returning the correct results because of the ternary expression in the lambda predicate? I assume so since taking the ternary out an generating separate ICriteria<Person> objects depending on the value of searchType is yielding correct results. However I am not so certain if this is because L2S cannot convert the ternary into a store expression, or if it is caused by something else.

  2. Since the method under test depends on a IRepository<Person> instance, how could I actually write a unit test around this? Mocking the IRepository<Person> in a unit test would not allow us to test the effects of the lambda on real underlying data. Creating a FakePersonRepository backed by some kind of IList<Person> would not reveal the actual defect either because the above lambda with the ternary expression returns expected results using linq-to-objects. Is there any way we could mock part of L2S so that we can possibly generate SQL using the lambda, and write assertions against that instead?

  3. Is this just something we have to do with an integration test and an actual L2S context with connection string, and cannot properly unit test? My definition of "integration test" means "connect to an actual database running in a separate process from the test runner using some kind of connection string", whereas "unit test" means "do everything in the test runner process".

danludwig
  • 46,965
  • 25
  • 159
  • 237
  • This is one reason why I never test or mock the 'raw' LINQ, but only components that return results - the components themselves are tested in context of a *real* database (and are only mocked when they are dependencies to other components being tested). – user2864740 Mar 25 '15 at 20:07
  • @user2864740 so are you saying "yes" to question #3? – danludwig Mar 25 '15 at 20:13
  • I believe that unit tests around L2E/L2S queries fundamentally *involve* the database as part of the 'unit' (and this unit is contained as part of the component that exposes the access). Things become much more iffy when dealing with transactions and cross-cutting contexts, etc - and those are definitely pushing the 'unit' envelope. But a unit test *can't mock what it is trying to test* .. or else it ends up testing nothing. – user2864740 Mar 25 '15 at 20:22
  • That is, I believe that L2E/L2S queries are unit tests are fundamentally unit tests *of the raw database access itself*, hidden behind a strongly-typed query syntax. – user2864740 Mar 25 '15 at 20:29
  • @user2864740 I see what you are saying, but when the test requires connecting to an actual database -- whether it be SQL, LocalDb, or whatever -- it crosses the line from a unit test to an integration test by my definition. Using my definition to draw the distinction between `unit` and `integration`, are you answering "yes" to question #3? – danludwig Mar 25 '15 at 20:31
  • I do not draw that distinction (which is why I cannot answer "yes") and I view it no different than verifying a raw SQL query works in a particular database environment (how *can* the query be validated in isolation?). But nobody has to agree with me. Just my pennies from my anemic-model EF/L2S experience. – user2864740 Mar 25 '15 at 20:37

1 Answers1

1
  1. Is it correct that the L2S (...) is not returning the correct results

It does return correct results, but not the results you expect, because you read the query as a human being and not as a compiler. The latter reads this:

tableToQuery.Where(p =>
    ((p.IsMale == false) && type.Equals("User", StringComparison.OrdinalIgnoreCase))
        ? p.User != null
        : p.User == null);

We humans tend to read this:

tableToQuery.Where(p => (p.IsMale == false) && 
     (type.Equals("User", StringComparison.OrdinalIgnoreCase)
        ? p.User != null
        : p.User == null));

2./3. how could I actually write a unit test around this?

Mocking and unit testing is virtually impossible. I stick to integration tests when I want to test the behavior of a data layer. For Entity Framework I collected some reasons for integration tests here. Much of it applies too LINQ-to-SQL as well.

Community
  • 1
  • 1
Gert Arnold
  • 105,341
  • 31
  • 202
  • 291
  • Regarding question #1, I actually made a mistake in the question. The previous (bad) code was actually `... (p.IsMale == false) && ...` not `... p.IsMale == false && ...`. I have updated the question. Does this change your answer to question #1? – danludwig Mar 25 '15 at 21:06
  • 1
    No, doesn't make a difference. – Gert Arnold Mar 25 '15 at 21:06
  • Well, it does make difference in that the faulty code read `p.IsMale == (false && ...)`, but it's still true that the predicates are interpreted differently by the compiler than what meets the human eye. If you add the parentheses as in my second example, you'd get correct (as in, expected) results. – Gert Arnold Mar 25 '15 at 21:12