Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

What would be the best approach at unit testing a model that integrates into an application that is tightly coupled to database?

The specific scenario here is a shopping cart - I'd like to be able to test the adding removing and retrieving of items from the cart as well as pricing logic etc. This in my mind all requires database access though I have read several times that database access should be avoided.

share|improve this question
    
Interesting that the answers which effectively say "rewrite your app code" get voted up –  AD7six Mar 7 '12 at 11:48
    
possible duplicate of Staying OO and Testable while working with a database –  gnat Feb 11 at 11:59
add comment

8 Answers

up vote 9 down vote accepted

Dependency injection is one way of handling this. You can set-up a test database to mimic the shopping cart, or you can even write some code that "confirms" the customer's transaction. Then at runtime, your software will pick which component to connect to.

Just don't connect to the production database for anything during testing!

share|improve this answer
1  
With DI and proper application design, you should be able to test without any database --- provided the mock that you inject provides enough detailed mocking of the back-end database. –  Peter K. Mar 4 '12 at 15:48
add comment

In unit test, you have to define the boundary of what you are testing. Unit testing is different from integration testing. If pricing logic is independent from Cart content, then you test that separately. If this is not the case, and all modules are tightly coupled, build a test environment that mimics the production as much as you could and work with that. I don't believe that short cuts and simulation helps on the long run.

share|improve this answer
    
+1 for defining the boundary of what you are testing –  Schleis Mar 9 '12 at 18:47
add comment

The model should not depend on a (concrete) DB. If it only knows an abstract DB (read "interface") which is handed to the model then you can replace the DB with a mock object.

In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior of a human in vehicle impacts...

share|improve this answer
add comment

I had a similar problem - I had no possibility to guarantee my test DB keeps the values. So in future I get e.g other prices.

I extracted the data I needed into a small sqlite-DB and used this DB for my tests. The Test-DB is now part of the setup of my unit-test.

share|improve this answer
2  
The point of unit tests is to test your code in isolation. If you use a sqllite db then its not in isolation. Also inconsistencies between databases can cause errors –  Tom Squires Mar 4 '12 at 14:01
add comment

"Best" is subjective, but you could just use a test db connection.

Use fixtures to load some test data (example products to buy) and then write the test case for the class/function you want to test.

share|improve this answer
    
Describing unit tests which test a function which acts on a database as integration tests is quite misleading @murph. –  AD7six Mar 4 '12 at 10:01
1  
Ok, now I'm deeply confused - if it involves a database its not by most definitions a unit test because its not self contained. If you have a database then you are running tests at a higher level, one that had dependencies one that looks at "combining" things. Regardless this is not a clear explanation to my mind of how to solve the problem. –  Murph Mar 4 '12 at 10:27
add comment

I built a plugin for Symfony 1.4 (PHP) to tackle this problem (among others). It is modeled after the way Django's test framework (Python) operates: the framework builds and populates a separate test database before each test starts, and it destroys the test database after each test completes.

I had a couple of concerns about this strategy, both in terms of performance (if the schema doesn't change, why not simply clear the data instead of rebuilding the entire structure?) and convenience (sometimes I want to inspect the database after a test failure, so don't destroy it indiscriminately!), so I took a slightly different approach.

Before the first test runs, the database is destroyed and rebuilt, in case there have been model changes since the last test. Before each subsequent test runs, the data in the database are cleared out, but the structure is not rebuilt (though a manual rebuild can be triggered from a test if necessary).

By selectively loading data fixtures in each test, one can create the proper environment for that test without interfering with subsequent tests. Fixture files can also be re-used, which makes this task much less onerous (though it is still my least favorite part of writing tests!).

In both test frameworks, the database adapter is configured to use the test connection instead of the "production" connection to prevent test execution from corrupting existing data.

share|improve this answer
add comment

I'd say, just go ahead and use fixtures to pre-load the data. It's how unit testing frameworks seem to work in general, when testing the manipulation of data.

But if you really want to avoid having to connect to a database of any sort and go by the overly-strict definition that unit tests don't touch anything outside of the code, take a look at object mocking - it may give you ideas.

For example, instead of dropping the SQL directly in the code where you need it, have a way to call a method that does only what that SQL does. UsePerson.getPhoneNumber(), for example, instead of SELECT phone_number FROM person WHERE id = <foo>. Not only is it cleaner and easier to understand at a glance, but during testing you can mock the Person object so that getPhoneNumber() will always return 555-555-5555 or something, instead of touching the database.

share|improve this answer
add comment

This is fairly easy to do with junit if a little long winded.

The "setup" should define and populate a set of temporary tables.

You can then perform the unit tests for all the update, insert, delete functionality.

For each test you call your update method then run some SQL to verify the expected result.

In the "teardown" phase you drop all the tables.

In this way you always run the same tests on the same initial data. If you keep the tables between tests they end up being "polluted" by failed tests, also, a consistent "insert" test is almost impossible as you need to keep inventing new keys on every test.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.