(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?*))))) | |