Unittest in Clojure doesn't compile when I test my own functions - unit-testing

I am new to Clojure and now I'm trying to use some unittesting.
I have a sample project with this structure:
core.clj in src/hello contains
(ns hello.core
(:gen-class))
(defn side-eq [x]
(if (< 0 (count x))
(= (.charAt x 0) (.charAt x (- (count x) 1)))
false))
core.clj in test/hello contains
(ns hello.core
(:use clojure.test)
(:require [hello.core :refer :all])
(:gen-class))
(use 'clojure.test)
(deftest side-eq-tests (
is (= false (side-eq "aws"))))
(run-tests)
when I execute tests, it throws
java.lang.RuntimeException: Unable to resolve symbol: side-eq in this context
When I test something like
is (= 1 1)
then everything works fine.
What is going on?

You should not have multiple files with the same namespace. Rename your tests to something else. The idiomatic name here would be hello.core-test In test/hello/core_test.clj

A variation which I prefer is to begin all testing namespacese with the tst.* prefix, which avoids the hyphen-underscore conversion & confusion (e.g. demo.core-test vs demo.core_test.clj. So your files look like so:
> d **/*.clj
-rwxrwxr-x 1 alan alan 1024 Jan 5 19:00 project.clj*
-rwxrwxr-x 1 alan alan 84 Jan 5 15:29 src/demo/core.clj*
-rwxrwxr-x 1 alan alan 248 Jan 7 12:42 test/tst/demo/core.clj*
and the code looks like:
(ns demo.core)
(defn -main []
(println "main - enter")
)
and
(ns tst.demo.core
(:use demo.core tupelo.core tupelo.test)
(:require
[tupelo.misc :as tm] ))
....

Related

Clojure: unit-tests defined with with-test are not actually run

Example adapted from the standard documentation for with-test:
(ns my-test
(:require [clojure.test :refer :all]))
(with-test
(defn my-function [x y]
;; (assert false)
(+ x y))
(is (= 4 (my-function 2 2)))
(is (= 7 (my-function 3 4))))
(test #'my-function)
The 'with-test' block and the 'test' blocks are evaluated correctly (using C-x C-e in emacs/cider).
But then if I uncomment the 'assert' line they still are!
Directly evaluating (my-function 2 2) fails.

How to implement TemporalAdjuster in Clojure

Clojure newbie, here.
I'm trying to implement a TemporalAdjuster in Clojure. I have the following:
(ns pto-calculator.logic.pay-periods
(:require [java-time :as j]))
(def next-pay-period
(reify java.time.temporal.TemporalAdjuster
(adjustInto [this temporal]
(let [local-date (java.time.LocalDate/from temporal)
day-of-month (.getDayOfMonth local-date)]
(if (< 14 day-of-month)
(j/plus local-date (j/days (- 14 day-of-month)))
(j/adjust local-date :last-day-of-month))))))
(defn get-next-pay-period [date]
(j/adjust date next-pay-period))
And I call it like this:
(ns pto-calculator.core
(:require [pto-calculator.logic.pay-periods :as p]
[java-time :as j])
(:gen-class))
(defn -main
[& args]
(p/get-next-pay-period j/local-date))
Today is March 2nd, so I expect get-next-pay-period to return March 14th, however, I'm getting an exception instead:
Caused by: java.lang.ClassCastException: java_time.local$local_date cannot be cast to java.time.temporal.Temporal
at java_time.adjuster$adjust.invokeStatic(adjuster.clj:64)
at java_time.adjuster$adjust.doInvoke(adjuster.clj:40)
at clojure.lang.RestFn.invoke(RestFn.java:425)
at pto_calculator.logic.pay_periods$get_next_pay_period.invokeStatic(pay_periods.clj:19)
at pto_calculator.logic.pay_periods$get_next_pay_period.invoke(pay_periods.clj:18)
My confusion is this: (j/local-date) returns an instance of java.time.LocalDate, which is a Temporal (according to the docs). So why wouldn't this work?
I've also tried:
(defn get-next-pay-period [^java.time.temporal.Temporal date]
...
But in that case I get this error:
java_time.local$local_date cannot be cast to java.time.temporal.Temporal
Is there a difference between java_time.local$local_date and java.time.LocalDate?
You aren't invoking java-time/local-date in your core namespace. You are passing a function to get-next-pay-period.
Instead of:
(defn -main
[& args]
(p/get-next-pay-period j/local-date))
Try:
(defn -main
[& args]
(p/get-next-pay-period (j/local-date)))

(:gen-class) and fully qualified symbol

I'm wondering why I need to include (:gen-class) to access imported functions from other ns without fully qualify them.
Example:
(ns project.core
(:gen-class))
(defn foo [] "foo")
(ns project.core-test
(:gen-class)
(:require [project.core :refer :all]))
(foo) ;=> "foo"
(ns project.core)
(defn foo [] "foo")
(ns project.core-test
(:require [project.core :refer :all]))
(foo) ;=> ComplilerException java.lang.RuntimeException: Unable to resolve symbol: foo in this context
(project.core/foo) ;=> "foo"
I couldn't get my head around this concept.
EDIT:
I had a typo in the require form. Idk why (:gen-class) solved this problem but now the project works again.
The (:gen-class) clause in the ns form is for Ahead-of-Time compilation (AOT). See https://clojure.org/reference/compilation
Something else must be going wrong in your setup. My example:
> d **/core*
-rw-rw-r-- 1 alan alan 98 Jan 13 16:15 src/fred/core.clj
-rw-rw-r-- 1 alan alan 47 Jan 13 16:15 src/tst/fred/core.clj
src/fred/core.clj
------------------------
(ns fred.core)
(defn foo [] (println "foo you"))
(defn -main []
(println "main - enter")
)
src/tst/fred/core.clj
----------------------
(ns tst.fred.core
(:use fred.core ))
(foo)
> lein test
foo you
I get the same results with the require version:
(ns tst.fred.core
(:require [fred.core :refer :all]))
(foo)
It also works if I paste your code into the repl:
~/tmp/fred > lein repl
Clojure 1.9.0
fred.core=> (ns project.core)
nil
project.core=> (defn foo [] "foo")
#'project.core/foo
project.core=> (ns project.core-test
#_=> (:require [project.core :refer :all]))
nil
project.core-test=> (foo)
"foo"

Can't take value of a macro

I have created a project using lein command.
This is my source file:
(ns database.core)
(defn movies[na rent qty]
(spit "e.txt" (.toString [{:na na :rent rent :qty qty}]))
(read-string (slurp "e.txt")))
This is my project file:
(ns database.core-test
(:require [clojure.test :refer :all]
[database.core :refer :all]))
(deftest movies-test
testing "movies"
(let [jun (movies "Dark-knight" 12 6)]
(is (= (get-in jun [0 :na]) "Dark-knight"))
(is (= (get-in jun [0 :rent]) 12))
(is (= (get-in jun [0 :qty]) 6))))
But when I try to run it i keep getting this error.
clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Can't take value of a macro: #'clojure.test/testing, compiling:(C:\Users\A\database\test\database\core_test.clj:5:1)
What could be the possible reasons for this error?
testing macro must be enclosed in its own pair of parentheses so that it was invoked
(deftest movies-test
(testing "movies"
(let [jun (movies "Dark-knight" 12 6)]
(is (= (get-in jun [0 :na]) "Dark-knight"))
(is (= (get-in jun [0 :rent]) 12))
(is (= (get-in jun [0 :qty]) 6)))))

Trouble referring to defrecord symbols in Clojure unit test?

Consider the following leiningen project core.clj file
(ns records.core)
(defn hello [] "hello")
(defprotocol my-sequence
(add [seqq item]))
(defrecord my-vector [coll]
my-sequence
(add [_ item] (conj coll item)))
I can compile this and test it in the REPL as follows:
records.core> (hello)
"hello"
records.core> (add (my-vector. []) 42)
[42]
But when I transcribe this into the leiningen unit-test file as follows:
(ns records.core-test
(:require [clojure.test :refer :all]
[records.core :refer :all]))
(deftest a-test
(testing "adding to a my-vector"
(is (= (hello) "hello"))
#_(is (= [42] (add (my-vector. []) 42)))))
The first test succeeds, showing that symbol hello is correctly moved into the records.core-test namespace, but the test of my-vector throws a compiler error (remove the #_ on the second line above):
clojure.lang.Compiler$CompilerException: java.lang.IllegalArgumentException:
Unable to resolve classname: my-vector, compiling:
(.../records/test/records/core_test.clj:8:22)
This does not seem to be a duplicate of this SO question because I am using require and refer, as the answer to that question suggested.
EDIT: the following also do not help
(add (records.core/my-vector. []) 42)
(add (#'records.core/my-vector. []) 42)
(add (##'records.core/my-vector. []) 42)
Unless my defrecord skills become rusty, you should first require the namespace and then import the defrecord.
(:require records.core)
(:import [records.core my-vector])