Fork me on GitHub

That (`parser` symbol) is the handle to the parser. defstate is a Mount thing, that is equivalent to def once your program is running.


@dansudol So, your server mutations modify a database, right? Datomic?


I tend to split my tests up as follows: 1. Tests that check the isolated I/O functions themselves. Preferably using something like Datomock to build a test data base I can very quickly “reset” to empty. 2. Tests that check logic (unit) 3. Tests that verify (using the same I/O tricks from (1)) that the stack behaves properly with respect to a read/mutation (e.g. permissions are honored and such). The vast majority of mutation tests are more (1) and look like: 1. Get a connection on an empty database with schema (made fast with Datomock and in-memory Datomic db) 2. Seed it with minimal data for the query/mutation in question 3. Run the helper in a test and verify it looks right. Then that helper is just used as the body of the query/mutation. The type (3) tests above are nice if you also want to check that your parser middleware (e.g. perhaps security layers) are also functioning properly. Here’s an example of (1):

(defn seeded-setup []
  (let [conn     (mdb/empty-db-connection)
        {:keys [tempids]} @(d+/transact conn s.seed/base-test-seed)
        db       (d+/db conn)]
    {:conn conn
     :user (d+/entity db (get tempids s.seed/test-user-name))
     :firm (d+/entity db (get tempids s.seed/test-firm-name))}))

(specification "get-address"
  (let [{:keys [conn firm]} (seeded-setup)
        result (address/get-address (d+/db conn)
                 {:address/id (-> firm :firm/locations first :address/id)})]
      (:address/street result) => "1000 Winchester Road"
      (:address/city result) => "Temecula"
      (:address/state result) => "CA"
      (:address/zip result) => "92591")))


where empty-db-connection is a test utility that is optimized for these (takes a few ms at most so tests are very fast)


(defn empty-db-connection []
    (dm/mock-conn @migrated-db))


where setup! is an idempotent function that makes sure the base migrated-db is set up once and only once…the mock-conn call takes a few microseconds to give you a “new empty database” with schema from the base one.


so now you can very very quickly write unit tests that run fast and verify your query/transact logic.


IMO, testing the parser layers themselves for a few things “full stack” is worth it, but not necessary for every one of these.


Most of your mistakes will be in the lower level interactions with the real db. I’ve done the same kind of testing for PostgreSQL, but the “fast setup” functions are a bit different (but possible). The basics there are that you do have to have PSQL running, but to get things to be fast: 1. Keep a test template database around that you can run migrations on. Schema is kept up-to-date, but otherwise empty. 2. DROP SCHEMA test CASCADE is a nearly instant operation that will wipe out a test database. 3. Create new test database from test template as the base, which is also very fast.


gets you reasonable speed for running real I/O tests


I do not recommend using an alt in-memory (e.g. h2) database for tests when you’re using something else for real.


yes this is super great to see you doing the mem db and database seeding tony


we just spent the week getting this setup and we have it pretty well down ( our seeding and our mem db that is )


and it is datomic we are using


so we make a new mem db every test and throw it away ( for now )


I would also recommend using something like gnl/ghostwheel to co-locate specs with your functions…catching where your tests are doing something diff than your production code is important


for sure .. we doing that as well with some functions ( not all .. but many ) ghostwheel is kinda hard sometimes ( i found when I wanted to do certain kinds of specifications ) but overall is good


the one thing i did not quite get from your example ( but almost got from the parser discussion ) was that in my test is how to test the mutation or pathom defresolver function .. i almost ( sort of ) ( maybe ) got it this morning where I got the handle to my pathom parser , then I said ( parser {} {:plan/id 1] [:plan/name} ) and it looked like it was being routed to pathoms parser and looking for the right resolver ( i think ) but it never hit the resolver ..


might be I just had wrong query .. but i think i am close


we are actually trying to test not so much the resolvers but the back end functions that are called for building transactions ( easy to test )


but in some cases I have a resolver that is kinda non standard or a server mutation that is just doing something i want to test and i want to hit it with a test to see that it returns the right thing


i looked at how pathom tests were setup ( in the pathom repo ) to get some ideas ( helped alittle ) and also peeked at fulcro tests as well


@dansudol that is it in principle, but your query looks like gibberish to me 🙂 Think you made a typo…but yes, that is the basic idea. The problem with testing against the real parser is you have to make sure all of the stateful dependencies of the parser are up and working…maybe that’s just a database, but maybe that is also redis, etc.


If you make all the stateful stuff be things passed in env, then you can just pass them.


ok .. i try that with the env ( passing in conn and stuff like that ) , and yes my query is gibberish because i not really sure how to format it correctly ( i should by now i know ) ( but was sorta copying from the pathom repo test i saw just to slap something in my tests to see what happened )