Unit Testing IQuerable<XXEntity>

Posts   
 
    
rlattimer
User
Posts: 3
Joined: 30-Mar-2009
# Posted on: 30-Mar-2009 23:11:21   

I am using Linq to LLBLGen and have run into a problem I was hoping someone could give me a hand with. I have been testing most of my calls to the entities by creating an IQuerable<XXEntity> object and passing it to a function where I add things like this:

internal static GetCompany(bool isActive, IQuerable<CompanyEntity> q) { q = q.Where(company => company.Active == isActive); //code removed }

For my test I then create a list of bogus CompanyEntities and pass in the .AsQueryable of that list and make sure the correct companies are returned.

This is where my question comes in. Say I need to both grab companies and their employes so the above code looks more like this:

internal static GetCompany(bool isActive, IQuerable<CompanyEntity> q) { q = q.WithPath(companyPath = > companyPath .Prefetch<EmployeeEntity>(company = > company.Employees); q = q.Where(company => company.Active == isActive); //code removed }

Now I can no longer just pass in a list of Companies .AsQuerable because I am using the WithPath. Is there any way to test this without actually hitting the database, or am I stuck inserting bogus records into the database to test with?

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 31-Mar-2009 05:24:37   

Are you trying to use in-memory queries or actually LLBL2LINQ? A don't understand you 100%.

David Elizondo | LLBLGen Support Team
Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 31-Mar-2009 09:51:09   

'AsQueryable' doesn't create a necessary LinqMetaData element to execute a linq query: a Queryable<T> needs a provider inside itself to be executed at enumeration. Calling 'AsQueryable' simply creates an enumerator of type IQueryable<T> but doesn't create a queryable of the right type, e.g. with expressions and with the provider inside itself.

Frans Bouma | Lead developer LLBLGen Pro
rlattimer
User
Posts: 3
Joined: 30-Mar-2009
# Posted on: 31-Mar-2009 16:22:40   

daelmo wrote:

Are you trying to use in-memory queries or actually LLBL2LINQ? A don't understand you 100%.

LLBL2LINQ. But when I test, I am just doing in-memory queries so I don't have to hit the database.

Otis wrote:

'AsQueryable' doesn't create a necessary LinqMetaData element to execute a linq query: a Queryable<T> needs a provider inside itself to be executed at enumeration. Calling 'AsQueryable' simply creates an enumerator of type IQueryable<T> but doesn't create a queryable of the right type, e.g. with expressions and with the provider inside itself.

Correct. Is there a way I can create a queryable of the right type to test with, without hitting the database. It would be nice if I could mock IQuerable<T> but there is no easy way of doing that either.

If I have not provided enough information please let me know and I can try to explain what I am doing in more detail.

daelmo avatar
daelmo
Support Team
Posts: 8245
Joined: 28-Nov-2005
# Posted on: 01-Apr-2009 04:29:53   

rlattimer wrote:

Correct. Is there a way I can create a queryable of the right type to test with, without hitting the database. It would be nice if I could mock IQuerable<T> but there is no easy way of doing that either.

You could mock IDataAccessAdapter, and then pass the mocked instance to a LinqMetaData instance and then simply access a property on that LinqMetaData instance, e.g. metaData.Customer to get an IQueryable<CustomerEntity>.

David Elizondo | LLBLGen Support Team
Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 04-Dec-2009 10:35:13   

Any tips on mocking IDataAccessAdapter? It doesn't look very easy trying to figure out which methods are called by a given LINQ query. confused

What would be really cool is if LLBLGen designer could generate entity test data and you could just point the DataAccessAdapter at it so that tests don't have to go to the database.

There's an MVC app called 'Kigg' and they've abstracted out the database such that Linq queries can be run against a mock database. Seems a shame to have the LLBLGen specific LINQ extensions such as 'WithPath' make doing this difficult.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 04-Dec-2009 15:55:07   

1) don't open old threads. 2) I don't really see the value in this, other than being able to use mocking. The problem is that no data is returned from your call, so what will you do in your test? Not to say that the linq provider wants to work with the real generated code, so mocking that away will simply fail.

it calls: - FetchEntityCollection(entities, filter, MaxNumberOfElementsToReturn, SorterToUse, PrefetchPathToUse, ExcludeIncludeFieldsListToUse, PageNumber, PageSize

  • FetchProjection(rawDataProjectors, new DataProjectorToObjectRowList(currentLevelData), executionElements.Fields, executionElements.Filter, toExecute.MaxNumberOfElementsToReturn, toExecute.SorterToUse, toExecute.GroupByToUse, toExecute.AllowDuplicates, toExecute.PageNumber, toExecute.PageSize);

  • KeepConnectionOpen

  • OpenConnection
  • ParameterisedPrefetchPathThreshold

though, as I said, do yourself a favor and drop this, it's likely not going to bring anything, simply because testing with a mocked database is simply useless: your tests have no value. Sure, you can 'write' the code first and then fill in the back-end but actually, that's really not going to pay off: the database isn't something you can abstract away, you have to think about the db from the beginning or you'll run into the dark side of ignoring it later on.

Frans Bouma | Lead developer LLBLGen Pro
Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 05-Dec-2009 02:08:49   

testing with a mocked database is simply useless

It allows one to test that one's queries filter/return the expected data. With T-SQL this is impossible without going to the db but with generic LINQ you can just knock up a small list of entity instances and select against that. Testing a database query is surely not useless!

the database isn't something you can abstract away

But that's exactly what the Kigg website (http://www.codeplex.com/Kigg) has done. They have an IDatabase which looks something like this for production...


    public partial class Database : IDatabase
    {
        public IQueryable<Category> CategoryDataSource
        {
            [DebuggerStepThrough]
            get
            {
                return GetQueryable<Category>();
            }
        }
      }

.. and like this for testing...


            var database = new Mock<IDatabase>();
            database.SetupGet(d => d.CategoryDataSource).Returns(new List<Category>().AsQueryable());

Perhaps their db and db operations are all quite simple though.

Sorry about opening a new thread. I always though it was the opposite - use the search because someone else has already asked the question. frowning

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 05-Dec-2009 12:10:05   

Ian wrote:

testing with a mocked database is simply useless

It allows one to test that one's queries filter/return the expected data. With T-SQL this is impossible without going to the db but with generic LINQ you can just knock up a small list of entity instances and select against that. Testing a database query is surely not useless!

what I meant was that linq to objects behaves differently than linq to <database>, simply because linq constructs have to be converted to SQL and this isn't always 1:1, so running the query not against a DB is IMHO not going to be useful, as the query on the DB might result in different results (and against the DB is what the real application will use, so using something else is only postponing unpleasant surprises wink )

the database isn't something you can abstract away

But that's exactly what the Kigg website (http://www.codeplex.com/Kigg) has done. They have an IDatabase which looks something like this for production...


    public partial class Database : IDatabase
    {
        public IQueryable<Category> CategoryDataSource
        {
            [DebuggerStepThrough]
            get
            {
                return GetQueryable<Category>();
            }
        }
      }

.. and like this for testing...


            var database = new Mock<IDatabase>();
            database.SetupGet(d => d.CategoryDataSource).Returns(new List<Category>().AsQueryable());

Perhaps their db and db operations are all quite simple though.

You don't get real-life numbers / scenarios. For an initial design it might work, but as soon as more and more complex code pops up into your application, the database is something to take into account, as a large percentage of your application will be spend inside the db.

Sorry about opening a new thread. I always though it was the opposite - use the search because someone else has already asked the question. frowning

We in general discourage it because a new post suggests the earlier posts in the thread are relevant for the new post, which isn't always the case, so to avoid miscommunication, we really want new threads instead of reopening old ones simple_smile

Frans Bouma | Lead developer LLBLGen Pro
Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 05-Dec-2009 13:45:25   

You don't get real-life numbers / scenarios.

Gosh, it is frustrating. If one appeals to high level ideas that one finds in books and in best practice example projects then one gets accused of not being realistic where as if one tries to be practical one is accused of lacking engineering sophistication.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 06-Dec-2009 12:21:27   

Ian wrote:

You don't get real-life numbers / scenarios.

Gosh, it is frustrating. If one appeals to high level ideas that one finds in books and in best practice example projects then one gets accused of not being realistic where as if one tries to be practical one is accused of lacking engineering sophistication.

err... don't put words in my mouth, Ian, I don't accuse you of anything, after all: I answered your question and gave you advice about what you might run into. I did that because I might know a thing or two about Linq and data-access and databases and thought that you might want to know these tips up front before spending a lot of time on things you later regret. I just wanted to give you advice of what you might run into, and you WILL run into these side effects. So better know them beforehand even if you don't like it. You don't have to take my advice, toss it into the trash if you will, it's not my project nor code you're working on. I just wanted you to know that with Linq, there's no such thing as a surrogate system in memory: linq behaves differently on the DB, and your mocked tests might run, chances are your db using code won't. If you're fine with that, by all means, go for it, I just wanted to make sure you do know the sideeffects up front.

Let me end with a quote from Dijkstra: "The required techniques of effective reasoning are pretty formal, but as long as programming is done by people that don't master them, the software crisis will remain with us and will be considered an incurable disease. And you know what incurable diseases do: they invite the quacks and charlatans in, who in this case take the form of Software Engineering gurus." before you think I mean you with the quacks and charlatans, I dont, I mean with them the TDD / agile gurus, who are seen by many these days as the bringers of the new way of how software should be designed, but actually are IMHO just bringing bullshit with a thin layer of chrome.

I can imagine it's hard to weed through the mixed messages and who to trust and follow. I can't make that decision for you. All I can say to you is that proven research and science isn't wrong. Mocking can help, but in general please do realize what you're actually doing: in practice it's not going to be as useful as it might sound on paper, especially with databases. Without mocking it's not really TDD, I know, but so be it. Is it more necessary to be pure TDD or is it more necessary to force yourself to think about what you're doing before actually doing it?

Frans Bouma | Lead developer LLBLGen Pro
Ian avatar
Ian
User
Posts: 511
Joined: 01-Apr-2005
# Posted on: 08-Dec-2009 07:42:47   

Thanks for your reply Otis. Sorry for putting words in your mouth. I think I got defensive because I was having a bad day - just moved house you see.

Is it more necessary to be pure TDD or is it more necessary to force yourself to think about what you're doing before actually doing it?

Yes I completely agree and that's one of the reasons I like to come on this forum - to discuss the implications of doing things and not to demand that LLBLGen conform to the latest fad.

Posts: 1263
Joined: 10-Mar-2006
# Posted on: 11-Dec-2009 23:00:17   

Otis, To jump in here, let me just ask for clarification.

You have an MVC app with a controller - that controller has a method of 'Add(somemodel)' and that adds the model to the database.

You are saying that for the unit test - you would run that controller method, let it hit the database, and then check the database for correct info?

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 12-Dec-2009 12:08:51   

yes.

the reason is simple: the db is not something you can mock away. Perhaps you've specified the wrong predicates somewhere, which are for example involved in the save process (concurrency), you have authorization setup and it should fail... etc.

Real life tests, so you don't get the situation where it doesn't work at deployment time.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 1263
Joined: 10-Mar-2006
# Posted on: 12-Dec-2009 16:26:56   

Wow...never heard anyone say that! Everyone preaches just the opposite and turns out it was good of me to IGNORE THEM as this is exactly what I have been doing.

smile

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39863
Joined: 17-Aug-2003
# Posted on: 12-Dec-2009 20:39:56   

Pay close attention who claims these kind of things. Often they're not people who write software for a living. I don't mind if someone claims that XYZ is a good method, perhaps it is, but my experience is that in practice, it doesn't matter. If you suck at writing code, your code will suck, regardless of how much best practices you follow.

About testability. I wrote for v3 ~50 unittests, mainly for situations where I wanted to test out the code before I would write the UI on top of it (otherwise it would take even longer to see if it worked in practice. So they were more or less for testing if some piece of code would work for a situation. If I would have used mocking, I wouldn't have got a clear picture: the mocks would perhaps made it work, while the test in practice would fail if it would have used a full stack of classes.

When migrating v2.x unittests to v3, I ran into an interesting problem: a test for System.transactions failed. This was because v3 was developed on a new machine and MS DTC was setup the wrong way. To test if my code would work under system.transactions I need to have everything in place to see it actually work. I therefore can't rely on 'abstracting away the environment outside my scope' like a database, as that won't work in practice.

Some bigwigs in TDD land hate me for it, but I like to preach 'thinking before doing': Analyze -> decide how to concur problem -> collect algorithms (existing or your own) -> prove your own algorithms -> implement algorithms -> done. This is not without the risk of making mistakes here and there, but it gives you a straight forward path which puts you into control when to take the next step and you make that next step based on thinking and making decisions. If you like uni=ttests, no problem. But writing code in an editor isn't the start of a process, it's the end. See my latest blogpost about algorithmia (posted yesterday) for an example simple_smile

Frans Bouma | Lead developer LLBLGen Pro