Webapp Testing¶
Strategies¶
- Pure functions, use generative testing and test cards, these are easy to write, but requires careful design from the author. At best, pure functions should be written in cljc, as they could be run in jvm.
- Devcards or storybook offers visual unit tests
- E2E testing leverages on javascript infrastructure. See taiko, cypress.
- Generative testing and another choice would be to record all the events the states and tests and leverage on google chrome protocols to listen for errors.
Snooping API Calls¶
- Use mitmproxy to spy on your API calls from your devices.
- Use ifconfig to check your machines IPs.
Chrome Devtools Protocol¶
You must lunch a browser with remote debugging port
google-chrome-stable --remote-debugging-port=9222 --user-data-dir=remote-profile
Using
clj-chrome-devtools, you
can listen to events
from the browser.
(ns user
(:require
[clojure.core.async :as a]
[clj-chrome-devtools.commands.log :as cdp-log]
[clj-chrome-devtools.commands.runtime :as runtime]
[clj-chrome-devtools.core :refer (connect)]
[clj-chrome-devtools.events :refer (listen)]))
(def c (connect "localhost" 9222))
(def chan-console (listen c :runtime :console-api-called))
;; (def chan-console-error (listen c :runtime :exception-thrown)) This is the other important domain and event
(a/go-loop []
(let [x (a/<! chan-console)]
(when x
(println x)
(recur))))
(runtime/evaluate c {:expression "3 + 5"}) ;; you can evaluate expressions
(runtime/evaluate c {:expression "1/0"}) ;; you should see an error
Re-frame and CDP¶
The problem is whenever the state of the application is valid, but the web browser is not. End to end testing is the process of replicating the user interaction with the application in order to test its behavior. My biggest feat is when the application enters an error mode from which the user can't recover. Normally, this situation should be caught at development time, however, as the code base grows and the code base more coupled, it is worth to develop a test suite in which we trust.
As much as unit testing is helpful to keep regression and verify some correctness properties, I found end-to-end (e2e) testing more appealing and worthy of my time, as they replicate my manual testing process with the browser.
There are many contenders in the domain (pupeeter and taiko for example), but my biggest hurdle with these solutions is they assume you don't have any control on the event system of the application: they force the tester to interact with finding the application by finding the right UI field and the right button and start inputing there.
Instead of finding the node and interacting with it, it would be much more interesting to dispatch re-frame events, and listen to the browser for any thrown errors.
The advantage is the reuse (or creation) the state machine interaction and the generation of user interaction travel.
The disadvantage is obviously testing speed, as we need to wait for events to render and possibly a lot of noise. It is awefuly slow. Moreover, some dispatched events might never be dispatched by users as they might not be able interact with the UI part. But, it allows you to replicate your manual testing process, which gives some confidence.
Strategy¶
The strategy is to create a test
module in our clojurescript app in
which several testing functions are defined, running a chrome devtools
protocol process which will call the test functions in a test browser.
The necessary steps are to adjust the shadow-cljs configuration, adding
the module to the html
in CDP, and call the export test function.
Playwright with nbbjs¶
Playwright (a fork of puppeteer) could be used in conjunction with nbbjs (a clojure repl with only node as dependency) to make `e2e` call as well. The advantage is the usage of premade containers, easing the deployment of the solution.
Links¶
- https://juxt.pro/blog/cljs-apps
- https://github.com/oliyh/kamera
- https://github.com/tatut/clj-chrome-devtools