5

I'm searching web and SO for keys like "Unit Test sqlalchemy" but all answers involves creating real database (sqlite, postgres) to test it. It's not Unit Test when you create real things.

Creating sqlite DB is no different from creating file on disk but surprisingly people wish to mock only the latter. I've tried mocking db import using setUp and sys.modules and it works but it mocks too much and using models as testing objects is no trivial (mocks does not support all needed things like next)

Is there any technique to mock database behaviour (creating db, commits, queries) but still have access to models to do basic checks on them ? Maybe there is some library to mock only connection ?

davidism
  • 121,510
  • 29
  • 395
  • 339
Dawid Gosławski
  • 2,028
  • 1
  • 18
  • 25
  • 1
    hi, your question is way too broad (too many possible answer, and no single authoritative one), and thus is not a fit for a SO question, so it's likely to be closed. – zmo Jun 14 '17 at 08:48

1 Answers1

5

Following up on my 'this Q is going to be closed', I believe there's no single authoritative way of doing it, and you need to make a choice on how you want to mock things up.

Basically, SQLAlchemy is an abstraction of the DB design and the query. So that means, you can place your mockups at different position in the abstraction:

  1. real db, with test data

you can setup an sqlite instance. The advantage of this solution is that you don't need to tweak/monkey patch anything in the python stack because you're changing stuff on the frontier of your system, the downside is that it's more work to maintain, and thus more risk.

  • with a data file

and store those data files in your code as resources. Then you load the right db for the test you're running. You'll need a tearDown() method to restore the db in its original state after the tests. The plus side is that it's easy to implement with little code, but it's a hell of work if you amend your model.

  • with an in-memory database

There you populate your data in the setUp() method, and have no db to store in your git or to take care of outside of your code. Though, you still have to maintain a lot of setUp() code.

cf that answer

That strategy would be the most commonly used and advised one.

  1. mock db access

Another way, that's being often used for mocking up HTTP connections, it's to serialise the output, and inputs of the communications between SA and the database. I'm not aware of a library doing that, but that would be a brilliant thing to implement.

Basically, just imagine a JSON file that contains all the requests sent to, and data returned from, the database. Each time you play your test, you're just replaying that JSON record.

When you make changes, you can record the I/O interactions and serialize them. A bit like the betamax library.

That strategy would be my favourite way of implementing it, but someone has to write the implementation for such a tool!

  1. mock/monkey patch individual elements of sqlalchemy

that would be painful and a lot of work, still many people try that approach. But I cannot think of a good reason to do that, as your goal is not to test sqlalchemy, but your own code, and you'd end up mocking up large parts of SQLAlchemy.

  1. abstraction of all db interactions in your code

if all db interaction is being abstracted by an interface in your own code, you can just replace the implementation of that interface with a mockup one and assert that you get the right parameters. The plus side is that it'd be easy to test and maintain. Though, I wouldn't advise to do that, because then you wouldn't check that you're using the SQLAlchemy library correctly.

zmo
  • 24,463
  • 4
  • 54
  • 90
  • It seems you did not read first part of my question. Creating any object for real, even in memory is not UT compliant. In UTs you do not rely on other things, even functions in same class (even simplest one-liners, just mock them all). So I'm looking for specific answer for 3rd point - libraries or technique to mock only specific DB. Unfortunately it seems SQLalchemy in Flask does a lot in just input so this is quite hard for me to find out. Abstraction is already done by Flask but their testing module also depends on real DB object. – Dawid Gosławski Jun 14 '17 at 09:33
  • I did read the first part of your question, but you do not want UT here, you want functional/regression testing. UT means that you're testing for a function `f(x)→y` you're validating all `y`s for all the possible values of `x`. That implies your function does not have side effects, and testing functions that calls SQLAlchemy methods imply rather huge side effects. So if you want to do as much UT as possible in your code, you do the solution #4, and then only implement functional and/or regression tests, i.e. #1, between your own abstraction and how you use SA. – zmo Jun 14 '17 at 09:43
  • Yep we ended up doing simple interface that abstracts the calls as it's very easy to maintain and test. You mix up two things - UT and Integration / regression Tests. We do not want developers to spawn containers to do just testing. We like to keep it simple - `python setup.py test` everywhere. We have separate tests based on behave that tests user logic in CI but it's different department taking care of that and devs just focus on coding/UTs. – Dawid Gosławski Jun 14 '17 at 13:27