Perpetuum
Perpetuum

Reputation: 230

Unit/integration testing nHibenrate query

Scenario: I need to write a complex nHibernate query, that would return projected DTO, but I want to use TDD approach. The method would look like this:

public PrintDTO GetUsersForPrinting(int userId)
{
     Session.QueryOver<User>().//some joins, conditions etc.
     //returns projected dto
}

Questions:

  1. Since the most common approach is to use in memory database for this kind of operations. Should I write integration test?
  2. If I am using in memory db can I write Unit tests?
  3. Is one test is enough?
  4. Since my integration test probably will check projection, how should I name it? "GetUserForPrinting_return_correct_DTO" seems too abstract and silly.

I ask because:

Upvotes: 3

Views: 640

Answers (2)

KarlM
KarlM

Reputation: 1662

This is not really a very good problem to learn TDD with. I assume you don't already know what the complex query looks like, and you want to use test-driven techniques to drive it out. Awesome :)

But let's see if I can answer your questions.

  1. Yes

  2. any test that includes a real db, whether it is in-memory or on-disk, is not a unit test. A unit test would use a mock db.

  3. Maybe - if you query is complex enough, then no.

  4. testGetUsersForPrinting or getUsersForPrintingTest or similar

Most probably I would drive out the query in a SQL interpreter, not in code. The aim would be to produce a series of integration tests against an in-memory db based on what I learn during this process. Start from the minimum possible DTO you can think of, and build up from there.

Finally convert the query into nhibernate calls, then make the integration tests pass.

Test-driven, but not really unit-test-driven.

If you are willing to accept maximum TDD discipline and deal with working slower and being more annoyed than usual, you can automate each integration test as you develop it and write code to make it pass. This will mean you are switching frequently among 3 levels of abstraction / editors / environments (direct SQL queries, integration tests, c# code) - I deal with this by setting up techniques to force myself to follow the right steps each time.

This last bit is why this is not a good problem to learn TDD with. You will need a lot of discipline you probably haven't forced yourself to acquire yet!

Good luck.


ok some concrete examples. I would modify your code sample to look like this

public PrintDTO GetUsersForPrinting(int userId, ISession session)
{
     var data = session.QueryOver<User>().//some joins, conditions etc.

     return data; // or whatever
}

In your unit test you would write

public testDTO()
{
    //Arrange
    StubSession session = .... setup a stub session, which returns hardcoded values

    // Act
    PrintDTO users = GetUsersForPrinting(111, session);

    // Assert
    Assert.That(users.size(), Is.EqualTo(1));    
    Assert.That(users.get(0).userId, Is.EqualTo(111));

}

In your integration test, you would use a real db, and your session object would actually connect to it, and the queries would be resolved against that db

Arrange-Act-Assert is a standard method for organizing unit tests. Generally you want as few Asserts as possible in a unit test. And you will have multiple unit tests. When you are writing a unit test, start by writing the Assert, then fill in the rest to make it compile/get the result you want. Make the test fail first, because then you know you have really delivered something when it passes.

In this example to implement a stub ISession you would derive a local StubSession class (only visible to the test suite) from ISession and just fill in the absolute minimum to get it to compile, and return the minimum data to get the test to pass.


To build up to your whole DTO - assuming you know what you want in your DTO - proceed, as you say in the comments, incrementally. Build up each part of your DTO a piece at a time, add a unit test for each piece.

Keeping track of this is another piece of TDD discipline.

Set yourself up with a TODO list - just a simple text file, or possibly a lengthy comments at the start of your test suite. List all the things you want to test e.g. zero results, one result, two results, 20 results. User id, whatever other pieces of information you need to have. If you are doing a complex query across tables or whatever add an todo item for each join, each part of the where clause, etc. Add items for ordering and paging etc if you are using those.

Pick the simplest things first. Only do one small thing (in a single red-green-refactor cycle) at a time. As you work through your list, you might want to break items up into smaller pieces, or you might think of additional things you need to do. Add them to the TODO list rather than working directly on them.

In this particular case I would swap - after each red-green-refactor cycle - into the SQL environment and/or the sqlite integration test to work out how to make the next piece work. I guess this is a sort of step between red and green - choose what you will test next, write the test (which fails obviously), fiddle around in SQL until you know how to make it pass, write the nHibernate calls to make your test green, then refactor.

Be aware some of the things you list might run out not to be necessary, or take too long, etc. It's good to write them down still, so you know what are not doing as well as what you are doing. Keep focused on your goal.

I tend to also develop a list of "smells" and/or refactorings that I can see I will want to do but am not quite ready for this cycle. Remember to minimise duplication/refactor your tests as well as your SUT (System Under Test).

It's a doing rather then seeing thing. The list of what unit tests you end up with, and the code they exercise, is not a very good description of the journey. Kent Beck's original TDD book is slim and will give you some good overall pointers, but not really about constructing queries.

Does any of that help?

Upvotes: 2

Low Flying Pelican
Low Flying Pelican

Reputation: 6054

Since the most common approach is to use in memory database for this kind of operations. Should I write integration test?

Using in memory database still is an integration test (because it actually tests if your query generates correct SQL and execute it against a database, see).

If I am using in memory db can I write Unit tests?

No, it would be an integration test

Is one test is enough?

Probably not, you should check each condition of your query, for example one test per one where clause, one for paging and one for sorting if applicable.

Since my integration test probably will check projection, how should I name it? "GetUserForPrinting_return_correct_DTO" seems too abstract and silly.

GivenUserForPrinting_WhenGetUserForPrinting_ThenMapToDTO would be a better naming

Upvotes: 1

Related Questions