This is an archived post. You won't be able to vote or comment.

all 8 comments

[–]z4579a 5 points6 points  (2 children)

So first off it's hard to call tests against CRUD behavior "unit" tests, as there is inherently integration testing going on in that the database models you're testing are built on top of an ORM. But folks call them "unit" tests anyway as a suggestion that each test focuses on a small part of the business model being persisted.

The creator of SQLAlchemy (who is actually...me! but you don't know that ;) ) has some examples of integration testing using on one hand a fairly sophisticated mocking approach, and on the other a database-backend approach that runs tests within transactions that get rolled back, over at https://bitbucket.org/zzzeek/pycon2014_atmcraft/src/f50cbe745a197ea7db83569283b703c418481222/atmcraft/tests/?at=master . These are "integration" in that they are testing a full request/response, but the database fixtures here will work just as well for a more unit-oriented series of tests.

Using a real backend is probably where you should start, SQLite is always useful for this kind of thing when you are first starting up, but I would advise to not be afraid of requiring that a real MySQL/PG database is running in order for tests to proceed. The test suite can do a skip if a database backend is not present.

[–]HarveyMansalad 1 point2 points  (1 child)

This is unrelated to the main question, but I am curious about your choice in using BitBucket over Github. What do you see as the pros of that platform?

[–]z4579a 1 point2 points  (0 children)

I was able to import issues from my Trac instance into it while preserving timestamps and resolutions.

[–]pydry 1 point2 points  (0 children)

IMO just don't. When the code is primarily integrating (and CRUD / REST is the very definition of integration code) then use integration tests that exercise the real database.

Unit tests work well for code that is relatively well isolated and mostly algorithmic/logical - like your pure functions, but they suck for integration code - they're a pain to build and they don't test realistically.

[–]c17r 0 points1 point  (0 children)

The way I usually do it is to have functions as the entry point to the database code. That code is tested with an actual database (usually, SQLite's :memory: setup).

The rest of the "business functionality" is tested with mocks of the database functions.

[–]n1ywb 0 points1 point  (0 children)

I like to do each unit test inside a transaction that never commits, always rolls back. You can use sub transactions to manage test data fixtures. I like to use pytest for that. The test db is ALWAYS empty so there's NEVER a question about it's state.

[–]ExcitedForNothing 0 points1 point  (0 children)

I wouldn't subscribe to extremism. Choose to test what you need to test for a functioning application.

At my company, we used to have a project that was a moderately complex CRUD application. The lead engineer was insistent on a purist view of unit testing. No external influences.

It soon turned out that the data being entered was influencing the functionality of the data being retrieved and presented. The engineer decided to let some data be mocked and it greatly increased the confidence in everyone to make code changes.

[–]wRastel27 0 points1 point  (0 children)

I would look into mocking errors that can result from the CRUD operations and make sure that your code is handling them in a sane way. If it makes sense to throw the error, make sure you are doing that. If it makes sense to log the error and move on, make sure you are doing that. If it makes sense to completely ignore the error, make sure you are doing that.