2

I'm trying to use gen/fmap with two random alphanumeric strings. Then I concatenate them with "@" and append ".com". But I'm struggling with the syntax.

First attempt:

(gen/fmap str (gen/string-alphanumeric) "@" (gen/string-alphanumeric) ".com")

But gen/fmap only takes two arguments.

Second attempt, where I group the second part doesn't work either

(gen/fmap str ((gen/string-alphanumeric) "@" (gen/string-alphanumeric) ".com"))

EDIT: I have a partial solution. It generates an email address, but the part before and after the @ are the same. Example: john@john.com

This is the partial solution

(def gen-full-string
  (gen/such-that #(not= % "") gen/string-alphanumeric))

(gen/fmap #(str % "@" % ".com") gen-full-string) 

I wrote gen-full-string because the empty string "" was crashing the code. Since I have parsing and plan to make validation functions, I didn't care about the empty string. I wanted to test core functionality not edge cases. Once I implement validation, I will probably remove gen-full-string. So the email generator would become (gen/fmap #(str % "@" % ".com") gen/string-alphanumeric)

triplej
  • 269
  • 4
  • 9

3 Answers3

4

Gary Fredericks has a library https://github.com/gfredericks/test.chuck that adds string regex generation to Spec. This allows you to use a regex that is as simple or as detailed as you want for email addresses.

Sean Corfield
  • 6,297
  • 22
  • 31
3

When I run this script:

clojure -Sdeps '{:deps {org.clojure/test.check {:mvn/version "1.1.0"}}}' /dev/stdin <<EOF

  (require '[clojure.test.check.generators :as gen])

  (def gen-email
    (gen/fmap (fn [[s1 s2]] (format "%s@%s.com" s1 s2))
              (gen/tuple gen/string-alphanumeric
                         gen/string-alphanumeric)))

  (run! prn (gen/sample gen-email))
EOF

I get this output:

"@.com"
"@.com"
"9p@VH.com"
"x1@Ws.com"
"23mF@93.com"
"b40@14.com"
"v0n@5Wskg.com"
"mNo@R85LuM.com"
"@.com"
"8Z84B9U0@f9QSJgM.com"
gfredericks
  • 1,312
  • 6
  • 11
  • Is this possible using the #() anonymous function syntax? – triplej Aug 25 '21 at 20:04
  • 1
    the `fn` in question is more awkward with `#()` because you can't destructure, so you'd end up with `#(format "%s@%s.com" (first %) (second %))` or alternately I guess `#(apply format "%s@%s.com" %)` but I don't find that very readable. – gfredericks Aug 26 '21 at 21:15
  • 1
    You might be interested in using `gen/let` instead of `gen/fmap`, people seem to find that easier to read and work with. – gfredericks Aug 26 '21 at 21:16
  • I was told from Erick Normand's guide that `gen/let` doesn't do shrinkage properly so it's better to use `gen/fmap`. – triplej Aug 27 '21 at 15:47
  • that might be true (I'm not as sure as I used to be), but the effect might not be severe enough to bother avoiding it. I had trouble actually finding a concrete case where I could tell the difference. – gfredericks Aug 28 '21 at 12:24
-2

I like test.check, but it takes a lot of time to understand the details. I created a helper library you may like. An example:

(ns tst.demo.core
  (:use tupelo.core tupelo.test)
  (:require
    [tupelo.gen :as tg]
    [clojure.test.check.properties     :as prop]
  ))

(dospec 9
  (do
    (newline)
    (spy :sample)
    (prop/for-all [w tg/word-alpha+]
      (spyx w))))

(dospec 9
  (do
    (newline)
    (spy :emails)
    (prop/for-all [w1 tg/word-alpha+
                   w2 tg/word-alpha+]
      (let [email-addr (str w1 \@ w2 ".com")]
        (spyx email-addr)
        )
      )))

with result:

-----------------------------------
   Clojure 1.10.3    Java 15.0.2
-----------------------------------

Testing tst.demo.core

:spy--tst.demo.core--line-011 => :sample
w => "b"
w => "lb"
w => "k"
w => "Y"
w => "mMWC"
w => "TzD"
w => "Nq"
w => "wQzPrF"
w => "HqEM"
{:result true, :num-tests 9, :seed 1629153846012, :time-elapsed-ms 1, :test-var "dospec-line-8"}

:spy--tst.demo.core--line-018 => :emails
email-addr => "k@v.com"
email-addr => "M@uL.com"
email-addr => "N@a.com"
email-addr => "OUr@B.com"
email-addr => "O@v.com"
email-addr => "UtZ@ndXS.com"
email-addr => "qhtDt@YLfE.com"
email-addr => "mLw@pFjKJJq.com"
email-addr => "hJ@DkCSnpAG.com"
{:result true, :num-tests 9, :seed 1629153846014, :time-elapsed-ms 1, :test-var "dospec-line-15"}

More details here. The above example uses my favorite template project.

Alan Thompson
  • 29,276
  • 6
  • 41
  • 48