(ns test.dev.n-plus-one-detection-test (:require [clojure.test :refer [deftest testing is]] [dev] [methodical.core :as methodical] [toucan2.core :as t2] [toucan2.tools.hydrate :as t2.hydrate])) | |
(methodical/defmethod t2.hydrate/simple-hydrate [:default ::some-silly-key-that-should-never-actually-be-hydrated!!!] [_model k row] (let [v (:one (t2/query-one {:select [[1 :one]]}))] (assoc row k v))) | |
(def ^:dynamic *cached?* nil) | |
A simple hydration function that simulates caching. The first time it's called, it hits the database, but it doesn't on subsequent calls. | (methodical/defmethod t2.hydrate/simple-hydrate [:default ::just-a-fake-cached-hydration-function!!!] [_model k row] (if @*cached?* (assoc row k :cached) (do (reset! *cached?* true) (assoc row k (:not-cached (t2/query-one {:select [[true :not-cached]]})))))) |
(deftest n-plus-one-detection-test-works (testing "it does actually detect N+1 queries" (is (thrown-with-msg? clojure.lang.ExceptionInfo #"N\+1 hydration detected" (t2/hydrate [{} {} {}] ::some-silly-key-that-should-never-actually-be-hydrated!!!)))) (testing "it detects N+1 queries even when there's just one item" (is (thrown-with-msg? clojure.lang.ExceptionInfo #"N\+1 hydration detected" (t2/hydrate [{}] ::some-silly-key-that-should-never-actually-be-hydrated!!!)))) (binding [*cached?* (atom false)] (testing "Hydration doesn't throw" (is (= [{::just-a-fake-cached-hydration-function!!! :cached}] (t2/hydrate [{}] ::just-a-fake-cached-hydration-function!!!)))) (testing "The 'cache' was populated" (is (= true @*cached?*))))) | |