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]
      (f)))

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.

Gotchas

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

Application Development by Rails Action Cable

The previous two blog posts introduced WebSocket and how to implement a WebSocket application on Ruby on Rails. This blog post digs deeper. It is a memo on creating a more realistic application by Action Cable.

Real-time App on Rails by Action Cable

The previous blog post, WebSocket on Rails by Action Cable, focused on WebSocket as a protocol. As in the previous post, by default, Rails app responds to WebSocket connection requests without any hassle. However, other than connecting and sending ping frames, it doesn’t do anything. This blog post focuses on an application side and explains how we can create a full-duplex, bidirectional app.

WebSocket on Rails by Action Cable

In the web application domain, we hear some protocol names. Absolutely, HTTP or HTTPS is the most famous protocol that all web developers know. Although there’s a mechanism of Keep-Alive, a single request/response sequence with a single client/server is all done by HTTP. The client initiates the HTTP request to the server. Once the client receives the HTTP response from the server, communication finishes. As far as HTTP is used, the server just waits and waits. Only when the request comes in, the server can send back some data to the client. This communication style is surprisingly capable of doing many things, so most web applications are satisfied with HTTP.