Created
September 13, 2019 09:48
-
-
Save philomates/3ae99114e9f39fa6030eeac2119bd08b to your computer and use it in GitHub Desktop.
match-with: a strange translation saga
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns match-with.a-strange-translation-saga-test | |
(:require [clojure.test :refer :all] | |
[matcher-combinators.core :as core] | |
[matcher-combinators.matchers :refer [in-any-order]] | |
[matcher-combinators.test :refer [match? match-with?]])) | |
;; ---------------------------------------------------------------------- | |
;; Lightning talk presented at the Clojure Berlin meetup on Sept 12 2019 | |
;; as a means to demo `match-with?` a new feature to matcher-combinators | |
;; ---------------------------------------------------------------------- | |
;; I work at a company that uses a fair amount of portuguese slang | |
;; so sometimes I get confused and mix up portuguese and english | |
;; Let's say I find a server endpoint that returns this: | |
(defn get-slack-word-freq [] | |
{:status 200 | |
:body [{:word "rataria" :freq 0.99} | |
{:word "xunxo" :freq 0.9}]}) | |
;; and now I want to exercise it with some tests: | |
(deftest naively-I-write | |
(testing "but I forget to translate some words" | |
(is (= ["hack" "rataria"] | |
(->> (get-slack-word-freq) | |
:body | |
(map :word)))))) | |
;; I don't like those diffs, so I pick up some matcher-combinators | |
(deftest I-try-again-with-help | |
(testing "and get a clearer failure message and order-agnostic matching" | |
(is (match? {:body (in-any-order [{:word "hack"} | |
{:word "rataria"}])} | |
(get-slack-word-freq))))) | |
;; luckily someone wrote a dictionary for me | |
(def portuguese->english | |
{"xunxo" "hack" | |
"rataria" "???" | |
"gambiarra" "monkey-patch"}) | |
;; so if I ever swap the language of a word, I can always run it through this | |
(defn translates? [a b] | |
(or (= a b) | |
(= b (portuguese->english a)) | |
(= a (portuguese->english b)))) | |
;; Now, I want something that matches results but doesn't care if I accidently | |
;; use the equivalent of the word in the other language | |
(deftest this-is-the-cleanest-I-could-do-after-10-minutes | |
(testing "not using matcher-combinators" | |
(let [;; boiler plate to help write this test | |
english->portuguese (->> portuguese->english | |
(map (fn [[k v]] [v k])) | |
(into {})) | |
language-agnostic (fn [a] (->> a | |
((juxt identity | |
portuguese->english | |
english->portuguese)) | |
(remove nil?) | |
set)) | |
expecteds (map language-agnostic | |
["rataria" "hack"])] | |
;; we check that every word in the result satisfies the language-agnostic | |
;; predicates we built | |
(is (every? | |
string? | |
(->> (get-slack-word-freq) | |
:body | |
(map :word) | |
(map (fn [e w] (e w)) expecteds))))))) | |
;; But with `match-with?`, a new feature in matcher-combinators, we can do a little better | |
(defn translates-match [expected] | |
;; the ergonomics of this isn't great, later I'll try to make it easier to define custom matchers | |
(core/->PredMatcher | |
(fn [actual] (translates? actual expected)))) | |
(deftest final-step-of-our-saga | |
(testing "redefining string matching to use translations via matcher-combinators" | |
(is (match-with? {java.lang.String translates-match} | |
{:body (in-any-order [{:word "hack"} | |
{:word "rataria"}])} | |
(get-slack-word-freq))))) | |
;; of course there are more "useful" applications, like non-exact number | |
;; matching, or forcing maps to be matched exactly instead of the default that | |
;; ignores extra keys | |
;; Check out the docs for more info: | |
;; https://github.com/nubank/matcher-combinators/#overriding-default-matchers |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment