1

I am trying to write a test using hspec involving postgres transaction rollback as with postgresql-simple's begin and rollback commands.

However, applying postgresql-simple's begin and rollback commands appears to cause my 'insert' command not to run and not affect my database at all.

Here is the relevant code.

import qualified Database.PostgreSQL.Simple as PGS
import Test.Hspec (describe, it, SpecWith, hspec, before, after)

testConnInfo :: PGS.ConnectInfo
testConnInfo = PGS.ConnectInfo
  { PGS.connectHost = "localhost"
  , PGS.connectPort = 5432
  , PGS.connectUser = "user"
  , PGS.connectPassword = ""
  , PGS.connectDatabase = "database"
  }

testConnection :: IO PGS.Connection
testConnection = do
  PGS.connect testConnInfo

runSpec :: IO ()
runSpec = do
  conn <- testConnection
  hspec $ test_addTask conn

test_addTask :: PGS.Connection -> SpecWith ()
test_addTask conn = describe "test_addTask" $ do
  it "adding_task_succeeds" $ do
    PGS.begin conn
    addTask conn "taskName"
    print "set breakpoint here"
    PGS.rollback conn
    return ()

I run this in ghci, setting a breakpoint at the "set breakpoint here" line; after the entry should be inserted and before the rollback. While paused at this breakpoint I query the database to see whether the entry has been inserted, and see that no entry appears to be inserted into the database by this code.

When I remove the PGS.begin conn and PGS.rollback conn lines, however, the code does insert the entry into the database.

Why does this code not insert an entry into the database with the 'begin' and 'rollback' lines in place?

How can I write hspec tests which begin and rollback so that my tests do not influence values in the database, and that actually run the commands?

mherzl
  • 5,624
  • 6
  • 34
  • 75
  • 1
    That's not how transactions work. Either a transaction takes place or it doesn't. If you don't commit a transaction from the DBs point of view, nothing ever happened. You never inserted a line into the DB. All you did was inform that you might want it inserted in the future, if nothing else goes wrong. – Cubic Sep 22 '17 at 22:45
  • @Cubic does that mean there is no way to test-call a database while using transaction rollback to prevent alteration? – mherzl Sep 22 '17 at 22:47
  • 1
    Well, you can use rollback to prevent changes. But if you prevent a change, then of course no change takes place. In any case, this makes it sound like you're trying to run your tests against production. Don't. Run against a dedicated testdb and just nuke that once the test run completed. – Cubic Sep 22 '17 at 22:49
  • @Cubic I will definitely take that advice and use a test db. While running there, is there a way to use rollback instead of nuking/truncating the db after each test? Maybe it's not, but I was under the impression that using rollback would be a more elegant solution. – mherzl Sep 22 '17 at 22:54
  • Like I said. Rollback isn't "undo". It's "never do in the first place". The entire point of transactions is grouping a bunch of individual actions into one atomic action. Setting up your test database should be automated anyway, so it shouldn't really cost you anything. For integration tests you might reasonably reuse the same database for multiple runs, and you don't need to rebuild the db if you never change it, but things may break during tests, so you need to set up your system to account for that anyway. – Cubic Sep 22 '17 at 23:00

0 Answers0