This. I would repeat with as strong an emphasis as possible the sanity of using a dedicated test database - how else can you test schema creation for example...
Dmitri writes: > I agree that wrapping the functions is a sensible approach. Using > wrap-transaction is precisely how I ended up doing it with the conman yesql > wrapper https://github.com/luminus-framework/conman > > The approach I took there is to have the generated functions use the > connection atom, and have with-transaction rebind it to the transactional > connection within its scope. However, the functions also accept an explicit > connection, and with-transaction also provides explicit access to the > transactional connection: > > (with-transaction [t-conn conn] > (jdbc/db-set-rollback-only! t-conn) > (create-user! > {:id "foo" > :first_name "Sam" > :last_name "Smith" > :email "sam.sm...@example.com"}) > (get-user {:id "foo"})) > > > > > This approach works for testing, since the connection can be passed > explicitly, but I would argue that in practice it's better to use a > separate test database instead of the dev instance. > > On Wednesday, August 5, 2015 at 11:11:19 AM UTC-4, James Reeves wrote: >> >> On 5 August 2015 at 14:03, Dmitri <dmitri....@gmail.com <javascript:>> >> wrote: >> >>> What I'm talking about is whether it's a better pattern to leave a >>> repetitive and error prone task to the user or encapsulate it in a single >>> place. The whole discussion boils down to following options. >>> >>> The first option is that we keep functions pure and the connection is >>> passed as a parameter by the person writing the code (the user). With this >>> approach the burden is squarely on the user to remember to pass the correct >>> connection to the function. This becomes error prone for things like >>> transactions where the developer has to pass the transaction connection as >>> opposed to the normal database connection. The worst part in this scenario >>> is that the code will run except it won't run transactionally. This is a >>> bug that is difficult to catch as it only surfaces in cases where the >>> transaction should be rolled back. >>> >> >> It's worth pointing out that you don't need to use dynamic or global vars >> to avoid this scenario. You could just remove the original database >> connection from scope: >> >> (defn foobar* [tx] >> (foo tx) >> (bar tx)) >> >> (defn foobar [db] >> (sql/with-transaction [tx db] (foobar* tx)) >> >> Or shadow the original binding: >> >> (defn foobar [db] >> (sql/with-transaction [db db] >> (foo db) >> (bar db))) >> >> Ideally you also want a way of testing this behaviour, even with dynamic >> or global scope. If it's critical to your application that database >> operations run in a transaction, you should have a way of verifying that. >> >> For instance, you might use a protocol to factor out the operations on the >> database: >> >> (defprotocol Database >> (wrap-transaction [db f]) >> (foo db) >> (bar db)) >> >> (defn foobar [db] >> (wrap-transaction db >> (fn [tx] >> (foo tx) >> (bar tx)))) >> >> This allows tests to be written to verify that foo and bar are called >> within wrap-transaction, and to verify our production implementation of the >> protocol correctly wraps the function f in a SQL transaction. >> >> If you're writing something that depends upon behaviour that can't be >> verified, you're going to run into problems no matter how you structure >> your application. >> >> >>> The alternative is to encapsulate the database connection management in >>> the initialization logic in the namespace managing the connection. This way >>> the query functions can be context aware and ensure that the correct >>> connection is used automatically. >>> >> >> Do you mean storing the database connection in a global var, not just a >> dynamically scoped one? >> >> In such a case, how do you run tests without wiping the development >> database? Do you run the tests in a separate process, or shut down the dev >> server before running tests, or do you not mind if your development >> database is cleared by your tests? >> >> - James >> -- Sent with my mu4e -- You received this message because you are subscribed to the Google Groups "Clojure" group. To post to this group, send email to clojure@googlegroups.com Note that posts from new members are moderated - please be patient with your first post. To unsubscribe from this group, send email to clojure+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/clojure?hl=en --- You received this message because you are subscribed to the Google Groups "Clojure" group. To unsubscribe from this group and stop receiving emails from it, send an email to clojure+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.