views:

477

answers:

8

What is the best way to unit test heavily database dependant .NET middleware? E.g. a process which reads data from multiple databases, manipulates it, and then combines and writes it to other databases?

Should the databases be filled with static data which is somehow reset on each unit test run? Should the whole SQL Server access be somehow mocked? Is it not feasible to unit test such an application in the real world?

+2  A: 

I think the best practice is really to Mock all access to database - just return some predetermined static data on call - you don't need to test how databases work - your need to test your unit behavior. Lesser your unit test interacts with outside - better it is.

With mocking your will be able to check that call to database is valid without really invoking it, so it's what you need i think.

Mihail
How would this work in practise? The application needs a sample set of data to work with. No data means it wont do anyting. What would be the best way to store this sample data and make it available to the function being tested. I dont think data-driven unit tests are the way to go (in case anyone suggests this) as that is about parameters to functions and return values, rather than the interation of the function with the database.
Ben Breen
Imagine I have a method readUserData() which invokes a database query. Your just set a stub on this query to return essential data, and verify how that query was invoked and how the data is processed by your unit (what is the result of it's work). This is how i understood this. I had some examples in my life using Moke objects but only in python...
Mihail
Look at MrTortoise solution with separated layers, it is the best way i think how you can use Moke database access.
Mihail
A: 

You should try to layer your code so that you can mock the operation of your functions.

The layer that does actually need to talk to the database could use a system of setting up the data and working with it within a transaction, which you would rollback at the end of the test.

Iain Hoult
+1  A: 

I'd recommend mocking the data access level. The benefit of using mocks in this instance include: 1) unit tests will run quicker. If they have to do the full work of connecting to the db, extracting data etc. then the cost will become expensive. Expensive tests = people stop running them/start losing faith in them! 2) You can test a vast range of scenarios without having to go through the hassle of setting up suitable test/static data that you have to ensure is always in the DB before the tests start. 3) taking away the external db system from the equation means you are testing just the .NET code you want to test. No external dependency.

You could either have a data access layer that does pure db interaction, and then mock that. Or, use mock SqlCommands etc in your .NET code.

AdaTheDev
+5  A: 

The answer is mocking

However the way to do this I have found is as follows.

Separate the DAL into 2 layers. The bottom simply performs atomic read and writes to the databases - these objects all implement a set of interfaces IReadFromDB and IWriteToDB.

Then you can create your read and write business logic in a higher DAL level but rather than reference the objects that will read and write to the database reference the interfaces and use properties to be able to substitute functionality. I tend to include the desired functional objects in the constructors so that things work 'out of the box' so to speak.

This will make it a cinch to 'swap out' the functionality and so to unit test the business logic.

As for testing the DB read and writes ... I haven't found a way that doesn't involve work. I usually use a different connection string to a copy of the database and then write data generation and cleanup code for unit tests to leave the state of the db the same as before and after.

Yes, its time consuming ... however it doesn't risk alienating a client. It depends on your priorities.

Someone else mentioned performance testing. I would not consider this to be part of a unit test. I usually do this with a test harness in combination with debug code simply because performance of small parts is often misleading - when you move to the big picture the parts that are actually casing problems are often not the parts that localised testing would flag in my experience.

John Nicholas
A: 

I think you should separate your concerns into different kinds of tests.

  • Write unit tests for the data manipulation logic implemented in .NET. Either mock the databases or completly separate the data massaging logic from the data access stuff, and just feed your data manipulation routine with pre-baked input data.
  • Create an integration test that exercises the whole data flow. This test should also be automated. Set up the source and destination database(s) (either from DB script or from DB backup) to a well-known state, run your data manipulation logic, then check the results.
  • You may also want to create performance and stress tests if you deal with lots of data. You can set up a database(s) the same way you did in the integration test, generate a bunch of test data, run your component and check either run times, or that it completes at all (eg. no concurency problems, deadlocks, etc.)
Vizu
A: 

I would abstract the data access layer and then mock it. With mocking you will be able to increase the code coverage of your tests. It will also prevent the idea of 'Test Cancer' that will happen by doing expensive calls to network/filesystem/database because people will stop running them.

You should have a few tests that call to the database but they should be limited as much as possible.

AutomatedTester
+1  A: 

Hi there.

I have two methods for unit testing code which has database dependancies:

  • Mock objects i.e. TypeMock
  • Custom SQL scripts running in the TestInitialise() and TestCleanup() subs.

With TypeMock, I can fake database data coming into my code, which helps me run my tests without having to worry about cleaning up the database, or restoring backups etc. I use mocking for testing code outside of the database logic, but still requires fake data from the database.

With custom SQL scripts, I can run SQL commands on the TestInitialise() sub and, when the unit test finishes, I have cleanup SQL run on the TestCleanup() sub. I use this method for testing code where the database logic is more complicated. For example, I might need to insert some rows into tables which will help with testing the code. Then on the TestCleanup() I will have my delete SQL commands which remove all the records that were inserted via the TestInitialise() sub.

Cheers. Jas.

Jason Evans
I like both your options. TypeMock (and similar) seem to offer the ability to add what I want to unit testing without major code or architecture changes. However I kind of feel if I had done it 'right' I wouldn't have to rely on these expensive 3rd party tools. Exactly what 'right' is remains open for discussion.
Ben Breen
A: 

In my work we use Nunit and Typemock for our Unit testing. The fact that we don't need to change our code for the tests is a big plus. Mocking the DAL is the way to go.