How to 'Unit' Test a database access layer - c#

I have written a .Net application which has many components, some of those components are database access layers which abstract from the rest of the components where the data comes from.
I have unit tested the rest of the components by mocking the database access layer. One way I have of testing the database access layers is to use create new empty databases on the test servers. This can be slow, and most would argue that it is not a unit tests as I depend on the database server.
What I think I want, is a mocked database which I can use to test my database access layer. The mocked database can be given a schema and process SQL commands as if it were a remote database, but in fact it is all in-memory. Does this exists? Or how else can I test my SQL and database <-> data model code.
To solve my problem you may want to know I am using SQL Server, versions 2008 and later, and my code is written in C#, running with .Net 4.5 and using Visual Studio 2013
Note: I do not want to use Linq2SQL/EntityFramework to replace my database access layer, as in my experience it results difficult to debug issues and performance problems.
I tried to phrase my question carefully to avoid people lecturing me on their beliefs in what should be tested and how, but perhaps to be a little more blunt:
I want to unit test my SQL, small changes to that have a big impact on the outcome of the program. I do have integration tests, but it takes much longer to create a release for the test environment than it does to tweak code and run the unit tests.
I appreciate people taking the time to read my question and respond anyhow.

I don't know if it's going to be the best answer, but. The way we're doing is that we're using SQLite, which is an in-memory database. There are a number of different ways to set it up, we use NHibernate as an ORM, and for that it is fairly easy to set up using FluentNHibernate, but I don't think it's much harder using any other framework either:
Fluently.Configure()
.Database(SQLiteConfiguration.Standard.InMemory())
.Mappings(m => ... )
.BuildConfiguration()
.BuildSessionFactory();
I think you can run queries against a SQLite database without any ORMs as well, e.g. using the base SqlConnection class.
This database can accept migrations, schemas, etc. It behaves very close to a proper MsSql database, we can run any DDL an DML statements against it, and it works fine. Since it's all in-memory, it's also pretty fast.

Related

Unit testing a sql string

Short Question
Is there something you can run sql commands that have JOINS and WHEREs that is not a DB
Long Question
I am putting in unit tests for a brown field win forms app.
I have complete freedom of choice on what kinda unit test framework I have
The problem I have is there is masses of SQL string statement in the code.
Think something like this
SELECT *
FROM Sale
INNER JOIN SaleItem ON Sale.ID = SaleItem.SaleID
WHERE ID = 5
It is parameterized, and has IF statements to build up the where, so it might be where CustomerId = 5 or DispatchDate was in last year.
The query is a lot bigger that this, and I kinda want to check that all the joins work and all the possible wheres work. Do think this might be me looking at the detail to much
I dont want to have to manage a database of data, which if the data changes it will break tests, and I'm scared that will root and people will just kill of the tests.
I want to run this sql against some object or a thing, that is NOT a DB and get a item.
It has to be smart enough to actually filter So it the Sale object was like the following table, it would only return the one with the ID 5.
ID DateDispatched CustomerID
1 1/1/1 5
2 2/2/2 6
5 3/3/3 7
I have thought of running sql command on datasets and XML, and relised that wont work.
I guess LINQ has spoiled me over that last few years cus I cant work out how to do this. And im afraid there is so much logic building up massive SQL statement, I have to put some tests on them.
Would be more than willing to hear about other options like moving the SQL to stored procedure in the DB, if you can recommend a good unit testing framework.
Now I don't like SQL being built in the app and would love to change it to entity framework, but its a 10 year old application and that's just not a option.
Okay some quick edits
The database is on SQL Server 2012, so stored procedures are a option, as in some places they use stored procedures.
Let me try to understand your problem.
You have got an winform application and you are writing unit tests for
this. But if you run the the test, you afraid it will going to hit the
DB and spoil the data. So you want some mechanism which allows to run
your unit tests but will not hit the actual database. Correct?
If I got your problem right, I suggest to separate out your db interaction logic and make it interface driven. Then you can create mock objects, wherein you define the expectation of your db interaction interfaces. So for example, if some GetSales() method is called, what should be returned from this method? and so on. Sharing some links on details about unit testing and mocking.
https://msdn.microsoft.com/en-us/library/ff650441.aspx
https://github.com/Moq/moq4
http://www.developerhandbook.com/unit-testing/writing-unit-tests-with-nunit-and-moq/
Testing a MVC Controller fails with NULL reference exception
A SQLite database is designed for exactly this kind of requirement.
The database is a simple file. The database driver reads from and writes to this file. You can run all the SQL queries and so on that you are used to against the database. (However, you might have problems if your SQL uses language or syntax specific to SQL Server.)
It is true that you have to manage a 'database' full of data. But you should be able to:
Write a script which quickly sets up all the tables. You might already have a piece of code which create a database with the same schema, or you might be able to dump one automatically.
Keep some test data around in CSV's, SQL files, or similar. This isn't easy, but it is very very useful. You should add the minimum and only build it up as the testing demands.
Check the whole SQLite database file into source control if you like.
Thanks for the question - I have been thinking a lot about testable database code recently, and I hadn't figured out a solution to this type of problem until your question made me realize I already knew it.
In my opinion, there are three approaches to make this kind of testing as painless as possible in the long-run:
Use an ORM or another wrapper layer, such as Entity Framework as you mentioned. This means that when testing you don't need a 'real database' at all - just a test double of your wrapper.
Only use standard portable SQL such as JOIN, SELECT, etc, with nothing which can't be run on a SQLite database. This can be very restrictive, as types vary so much between DBMS.
Use SPs exclusively as an interface to your database. This means that your test double only has to recognize which SP is being called, and respond correctly to that. I personally don't like this approach as I think lots of untested, unversioned business logic ends up in the SPs.
Here is an option that we use for our unit testing with MS test
Use the TransactionScope from the System.Transactions namespace
in your TestInitialize method Create an instance of the transaction and in the TestCleanup method dispose of it. You can do the insert into the db in the test initialize method or in the individual test methods
[TestCleanup]
public void testClean()
{
_Trans.Dispose();
}
[TestInitialize]
public void testInit()
{
_Trans = new TransactionScope();
}
[TestMethod]
public void TestQuery()
{
// arrange
//' insert data
// act
Obj Target = Obj.New();
// Assert
Assert.AreEqual("someValue",Obj.SomeProperty);
}
Okay I took the old adage of if you cant change your employer, change your employer.
I think tsqlt http://tsqlt.org/ would of been the best fit for this exact problem.
As Entity framework would not be allowed and they had so many dependency tables. Which would let me mock tables move all logic to stored procedures and mock the tables. Kinda mix of Yogi and JWG answer.

wcf service testing with fake data

I have been looking for solution for a few days already and could find anything that would help to solve my problem.
I have a WCF service for which I have to make some unit tests. The problem is that service takes data from database in this way:
using (var context = new MyProjectEntities())
{
//here goes the actions
}
MyProjectEntities is autogenerated from edmx model i guess.. (Database first)
So this way it takes all the data from the database and operates on it.
My question is: whats the correct way to feed service with fake data for testing, instead of data from database?
The most trivial way is to use a live database. This isn't too flexible, because you need a new database in a fixed initial state for every single run, and also multiple developers can't use the same database at the same time.
What we do at my company is this: use a single-file database, namely SQL Server CE. If your code is database-engine independent, this can totally work, just change your connection string and you can even put your database to a fixed state by copying a template datafile to the right place. This isn't a really isolated unit test, but it's very simple to implement, it doesn't have the above problems, and you basically get what you need in the end. If your code is database-engine dependent, now you have one more reason to use an ORM solution like NHibernate or Entity Framework.
The best, most flexible, and also most complex solution is using a dependency injection or mocking framework. This is textbook stuff, there's tons of literature on that, it will give you all the flexibility there is.

How would i unit test database logic?

I am still having a issue getting over a small issue when it comes to TDD.
I need a method that will get a certain record set of filtered data from the data layer (linq2SQL). Please note that i am using the linq generated classes from that are generated from the DBML. Now the problem is that i want to write a test for this.
do i:
a) first insert the records in the test and then execute the method and test the results
b) use data that might be in the database. Not to keen on this logic cause it could cause things to break.
c) what ever you suggest?
You should choose option a).
A unit test should be repeatable and has to be fully under your control. So for the test to be meaningful it is absolutely necessary that the test itself prepares the data for its execution - only this way you can rely on the test outcome.
Use a testdatabase and clean it each time you run the tests. Or you might try to create a mock object.
When I run tests using a database, I usually use an in-memory SQLite database.
Using an in memory db generally makes the tests quicker.
Also it is easy to maintain, because the database is "gone" after you close the connection to it.
In the test setup, I set up the db connection and I create the database schema.
In the test, I insert the data needed by the test. (your option a))
In the test teardown, I close the connection to the db.
I used this approach successfully for my NHibernate applications (howto 1 | howto 2 + nice summary), but I'm not that familiar with Linq2SQL.
Some pointers on running SQLite and Linq2SQL are on SO (link 1 | link 2).
Some people argue that a test using a database isn't a unit test. Regardless, I belief that there are situations where you want automated testing using a database:
You can have an architecture / design, where the database is hard to mock out, for instance when using an ActiveRecord pattern, or when you're using Linq2SQL (although there is an interesting solution in one of the comments to Peter's answer)
You want to run integration tests, with the complete system of application and database
What I have done in the past:
Start a transaction
Delete all data from all the tables in the database
Setup the reference data all your tests need
Setup the test data you need in database tables
Run your test
Abort the transaction
This works well provided your database does not have much data in it, otherwise it is slow. So you will wish to use a test database. If you have a test database that is well controlled, you could just run the test in the transaction without the need to delete all data first.
Try to design your system, so you get mock the data access layer for most of your tests. It is valid (and often useful) to unit test database code, however the unit tests for your other code should not need to touch the database.
You should consider if you would get more benefits from “end to end” system tests, with unit tests only for your “logic” code. This depend to an large extent on other factors within the project.

Creating Database Mocks in ASP.NET MVC using Data from Existing Database

I have an existing ASP.NET MVC application with some sample data in the SQL Server database, which is working fine..
Assuming I have all of the necessary repositories and IOC in place, is there a tool that will extract the data from a group of tables, and "freeze-dry" it into a mock object (perhaps using an XML file to store the data), so that I can detach the database and use the mock data for my unit tests?
Depending on what exactly you are trying to test there might be different approaches.
If you want to test the data access logic then this is no longer unit test but integration test. For this kind of tests it is a good idea to be able to easily replace the real database with some lighter maybe even in-memory database like SQLite which will be reconstructed before running each test. If you are using an ORM this task is easy. All you need to do is to generate SQL scripts (INSERT INTO...) from your existing database, modify and adapt the dialect to SQLite (if necessary), read and inject into a SQLite file and finally all that's left is to instruct your data access layer to use SQLite dialect and connection string for the unit test.
Now if you are not using an ORM and your data access logic is tied to MSSQL things get uglier you will need a live database in order to perform those integration tests. In this case I would suggest you duplicate your real database which you would use for the tests by modifying only the connection string. Once again you will need to properly setup and teardown (preferably in a transaction) this test database in order to put it into a known state for the tests.
If you want to test code that depends on those repositories (such as your controllers for example) you don't need to even bother about mocking the data as your controllers depend on abstract repositories and not the real implementations (aren't they), so you could easily mock the methods of the repository in order to test the logic in the controllers.
This is actually a well known "test smell":
http://xunitpatterns.com/Obscure%20Test.html#General
From:
2098937: Proper way to Mock repository objects for unit tests using Moq and Unity
I don't know of a direct way to do what you're asking for, but MSSQL supports export to CSV, Access, and Excel. Although, this require you to change the Data Access Layer in your in your mid-tier, and furthermore, I don't feel this answers your question:
"freeze-dry" it into a mock object
This I'm not sure of. Is it feasible to just restore a backup of the database either on the same SQL server as a new database, or possibly on a dev SQL server?

Clear NHibernate database fast

I am using NHibernate for ORM, and everything works fine.
Now I started to write some unit-tests (using the DB, I do not want to put tooo much effort in abstracting this away, I know its not perfect, but it works..).
I need to be sure that the DB is completly empty for some tests. I can, of course, create the whole DB. But that seems to be overkill and I think it takes longer...
Is there a DELETE_ALL command which clears all tables, I can use in NHibernate?
Chris
EDIT: A short update, I decided to go the SQLite way, no problem to change this with NHibernate. There are some pitfalls, I am using this config, and it works. Otherwise you might get "table not found" errors, due to nHibernate closing the connection while in session, resulting in a "lost" database...
For your convinience: Copy and paste...
.Database(SQLiteConfiguration.Standard.ConnectionString("Data Source=:memory:;Version=3;New=True;Pooling=True;Max Pool Size=1;")
.Raw("connection.release_mode", "on_close"))
.Mappings(obj => obj.AutoMappings.Add(_config.APModel));
Drop and recreate the database. You can use schemaexport:
var export = new SchemaExport(config);
export.Drop(false, true);
export.Create(true, true);
Sql lite in memory runs faster for tests than a "normal" database, but the disadvantage is that the sql-lite dialect can be different than the sql dialect in production.
I would recommend you check out ndbunit. It's not a NHibernate-specific, but I've used it for testing NHibernate projects in the past and it works well. Basically, it provides functions to clear the database, prefill it with test data, or restore it to known states after each test. You just have to provide an XSD of the database schema, and optionally some XML data for pre-filling.
I believe I first saw this in the Summer of NHibernate screen-cast series, so check those out to see it in use.
You're not writing unit tests (i.e. tests that test one unit), you're writing integration tests where units interact (i.e. with your database).
Part of your test infrastructure could run a sql script that does one of the following:
Drop db and recreate.
Truncate all tables.
Ideally, you do want to put a bit of work in abstracting the db away, especially since you have NH which makes it much easier than some other frameworks.
Use an in memory database like SQLite, and setup the necessary data in it before each test. The initial setup takes a bit of time, but each test runs very fast afterwards and you can make sure that you start off with a clean slate. Ayende has a few blog posts about how to set it up.
Just in case the Drop/Create DB does not suit your needs (like if the db contains object that NHibernate is not aware of, like SPs, functions etc) you could always make a backup point with the DB empty and after you're done testing just restore to that point

Categories