HackerRank Code Testing by Clojure

Published: Aug 29, 2019

Recently, I decided to give Clojure a try. This post is about testing HackerRank problems on a local env.

I used to write Clojure code for my day job, however, no Clojure at all for two years. Instead, almost all were done by Python. I became familiar with Python’s functional programming world now. Aside of ML/DL projects, I did bunch at HackerRank, leetcode or sort. After a while, I thought, “maybe I can solve problems by Clojure like Python…”

Luckily, HackerRank supports Clojure. So, I started.

Soon, I hit the wall. I wanted to test a code. Okay, writing test is not hard. Something quite hard was the HackerRank platform. Test data comes in from STDIN. Something output to STDOUT is tested. For many problems, it’s effective to have read-line and println in the code side.

I did quite a lot of research and trials so that the code can be tested without any modification on HackerRank platform. This experience, setting up a testing environment was a good Clojure study. That’s why I wrote the blog post. This post may help other Clojure newbies to figure out how.

Binding STDIN

Clojure has clojure.core/binding function. This function is used to create new bindings, which is like, replacing the current bindings. In Clojure, STDIN is bind to *in*. So, what should be done is to bind *in* to something else.

Next question is what should be “something else.”

HackerRank test case is provided by a file. People can get the test data file if they pay 5 Hackos. The “Hackos” are points people can get when their solutions successfully pass all tests. Unless the problem is a very entry level, the test case file tends to pretty big. It’s unable to write the test data in the code often. For this reason, I wanted to get the test data from a file and bind it to *in*.

What I did was:

  1. create a file under resource directory
  2. read it as a resource
  3. convert it to a reader
  4. bind the reader to *in*
(require '[clojure.java.io :as io])
(with-open [rdr (io/reader (io/resource n))]
    (binding [*in* rdr]

Since the project was created by the leningen, the resources directory is already on the path. Just to give a relative file path as n, the file is read. The f is a function which consumes data from STDIN.

Catching STDOUT

Now, reading from STDIN was cleared. Next will be to catch STDOUT. Clojure has a convenient function clojure.core/with-out-str. Compared to a reading part, catching STDOU was much easier. For example:

;; define function which prints to stdout
> (defn say-it []
    (println "seriously?")
    (println "can't believe"))

;; assign STDOUT to "out"
> (def out (with-out-str (say-it)))

;; what's in the "out"
> out
"seriously?\ncan't believe\n" 

The same idea for the reading can be applied to STDOUT – printing out to a file under resources directory. However, in general, the expected output is simple for the HackerRank problems. At this moment, with-out-str function satisfies my needs.

Wrapping Tests

So far, STDIN and STDOUT work well to fit with the HackerRank platform. However, to bind resource reader to STDIN, I should write with-open ... in every test case. I want to avoid writing the same lines again and again. After searching online, I found clojure.test/use-fixtures function. This function works like Unit test’s setUp()/tearDown() methods. As the Clojure API document explains:

(defn my-test-fixture [f]
        (create-db)   ;; setUp
        (f)           ;; test
        (destroy-db)) ;; tearDown
(use-fixtures :once my-test-fixture

Define the fixture function, then call use-fixture function. The tests in the same namespace are all wrapped by the fixture.

Below is an example test by the use-fixture function.


The function use-fixture made my test code simple. It’s good. However, it has a downside. Some problems provide multiple test cases, which means multiple resource files on my test environment. Since only one use-fixture is applied to all tests in the same namespace, my wrap-test and fixture design don’t work nicely. There should be a way to cope with this problem. It will be my next challenge.

If you are interested, my solution and test code for 30 Days of Code are in my repository, thirty-day-code.

Latest Posts

Setting Up GCP Instance for Deep Learning

This post is going to be very different from what I write here. The content is a memo how I create a GCP (Google Cloud Platform) instance for Deep Learning. While I study algorithm stuff, I also have been studying Deep Learning. In my early days, I tried to train my Deep Learning model only on a laptop. My laptop is 2012 model MacBook Pro, so I would say it is reasonably fast. However, when it comes to Deep Learning, the training was quite painful on the such machine. Often, I ran the training over night to get a disappointed result. Still, I didn’t use any of pricey cloud environment since it was my personal study unrelated to my day job. I wanted to save money.

Complete Binary Tree

Problems which ask a binary tree traverse, add/delete nodes, etc. are popular in algorithm questions. The binary trees are often just a binary tree or binary search tree. Sometimes, the problem pinpoints a particular type of a binary tree, for example, a balanced binary tree or complete binary tree.

Catalan number

What is Catalan number ? The Catalan number belongs to the domain of combinatorial mathematics. It is a sequence of natural numbers such that: 1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, 2674440, 9694845, 35357670, 129644790, 477638700, 1767263190, ... The sequence appears in counting problems. Wikipedia has the details about the sequence: Catalan Number.