Fork me on GitHub
#beginners
<
2023-02-08
>
Flash Hu02:02:31

How could I mock an HTTP request in a Unit Test? As I googled and found https://github.com/ring-clojure/ring-mock, but I’m not sure it’s the correct one to use.

2
Flash Hu02:02:47

Here is the code I want to mock: There’re two HTTP requests which I’d like to mock, fetch-volcanic-user-by-id and get-candidate-by-email since they might be 404, I’d like to protest the login with Unit Tests.

hiredman02:02:03

I usually don't "mock" it, easy enough to spin up a real http server and hit it in tests

Flash Hu02:02:24

Yes, I did the same as you. But I’m worried that some month later, I’ll forget any edge cases I added today, or when I refactor this block of code, I’ll break it, then it’s better to protect them with Unit Tests.

hiredman02:02:44

You may be making a distinction in types of tests that I don't make

hiredman02:02:27

Or you may be misunderstanding me

seancorfield02:02:58

There are a number of things you can do to make this code easier to unit test. For example, you could pass in the two functions that make the HTTP calls -- then you can pass in "mock" functions in the tests and have it call those instead.

hiredman02:02:12

I mean, I have a deftest in my test suite, and within the deftest I start the webserver, interact with the server via http and make assertions about the results, then stop the webserver

Flash Hu02:02:47

Thanks, passing the function inspired me.

seancorfield02:02:10

You could also split the "volcanic" part from the "candidate" part so you have two smaller functions to test.

Flash Hu02:02:37

> within the deftest I start the webserver… Oh, it’s my bad. In the code block above, I do real HTTP requests, as I’m not building a server with API to test. Actually, I’m working on a script which fetches different resources remotely and then does some logic.

seancorfield02:02:42

Right, but hiredman was suggesting spinning up "mock" servers in your test to call into (instead of the actual services). Which is what we do at work (we work together).

seancorfield02:02:27

It depends what level you want to mock things at and why.

seancorfield02:02:23

(in other words, there's no one right way to do this 🙂 )

Flash Hu02:02:12

Oh, I see. And could you suggest me some tutorials for setup a “mock” servers locally? About the level, I think I’m not clear about it crying-laughing-joy-blood

seancorfield02:02:32

Hmm, no idea about that, sorry.

seancorfield02:02:29

(Hiredman and I have both been doing Clojure for a decade so we're not so good at recommending beginner-level tutorials, unfortunately!)

Sam Ritchie02:02:46

Longer than that :)

Sam Ritchie02:02:13

Time flies, but it’s gotta be closer to 13 right?

seancorfield02:02:57

Haha... yeah, probably... I think we both got started in 2010?

Sam Ritchie02:02:24

@U04V70XH6 yup that sounds right

Flash Hu02:02:43

LoL, and thank you both again, as I learned a new level to test my code - setup a “mock” server, then I can test them locally without touching the internet.

Sam Ritchie02:02:56

@U04GM4FRG13 there is one idea, http libraries will each have their own ways of testing themselves, so you can poke at their tests to see what they’re doing

Sam Ritchie02:02:13

Or if they’re kind then they’ve documented an approach for you as above

seancorfield02:02:14

Documentation in general and tutorials in particular have changed a lot in that time. Hopefully for the better.

👍 2
Sam Ritchie02:02:26

@U04GM4FRG13 my favorite style is when a library lets you plug in some sort of “backing store”, and you can use the real network client in prod, and then an atom, say, in tests

Flash Hu02:02:38

> so you can poke at their tests to see what they’re doing Thanks!

Sam Ritchie02:02:17

With that kind of design, you can set up the “server” using a hash map in an atom, do your stuff and then check the mutated value afterward (or for GET tests check that you get out what you populated the map with)

Flash Hu02:02:12

make sense. It’s the best way to have two different access behaviour between the prod and local.

Flash Hu02:02:47

Here is the code I want to mock: There’re two HTTP requests which I’d like to mock, fetch-volcanic-user-by-id and get-candidate-by-email since they might be 404, I’d like to protest the login with Unit Tests.

Tuomas-Matti Soikkeli14:02:30

is it possible to evaluate that I have removed a function

andy.fingerhut14:02:09

Do you mean something like you have performed (undef foo) and you want to ensure that foo is no longer bound to any value?

andy.fingerhut14:02:53

Ugh, sorry, no morning coffee yet. There is no such thing as undef .... Not sure where my brain got that.

andy.fingerhut14:02:51

Here is a short REPL session in Clojure/JVM that might show pieces that lead you to what you are looking for:

andy.fingerhut14:02:53

user=> (def foo 1)
#'user/foo
user=> foo
1
user=> (contains? (ns-map 'user) 'foo)
true
user=> (get (ns-map 'user) 'foo)
#'user/foo
user=> (ns-unmap 'user 'foo)
nil
user=> (contains? (ns-map 'user) 'foo)
false

andy.fingerhut14:02:25

If. you want something similar in ClojureScript, (a) I'm not sure if it is possible, and (b) I'm not familiar enough with the differences here between ClojureScript and Clojure/JVM to know the changes required

andy.fingerhut14:02:15

ns-unmap is perhaps what you mean when you say "removed a function" ?

Rupert (All Street)14:02:18

I assume that once you have a reference to a function you can call it until you release the reference.

(let[my-plus clojure.core/+] (my-plus 1 2))

andy.fingerhut14:02:04

Yes, but I would generalize that to "once you have a reference to any value, function or otherwise, you can still access/use that value until you have no references to it"

andy.fingerhut14:02:35

There is nothing unique about functions (when viewed as values) there.

👍 2
Tuomas-Matti Soikkeli14:02:16

I mean I’d like to make sure that a function that I removed doesn’t get called. I changed the calling side too to not to call this function and perhaps the dangling function gets garbage collected at some point… So perhaps this is just uneccessary thinking.

Rupert (All Street)14:02:17

Yes - the function will get garbage collected when the garbage collector runs and there are not references remaining to the function.

Rupert (All Street)14:02:55

Functions take very little space in memory so probably not worth worrying about it from that perspective. If you want to be certain you can kill the REPL and start a new one.

jumar18:02:44

For Cider users, there’s cider-undef

Ivan Quirino18:02:15

I have one simple doubt, is that ok to do something like this?

"some text to describe something"

(defn somefn [a] a)
or should I always use comments?

pavlosmelissinos18:02:34

Rogue top-level strings don't make a lot of sense... Docstrings are generally a better construct when it comes to describing parts of code. Do you have an actual example? What would that text look like and what would it describe?

Ivan Quirino18:02:47

I did it by accident, not intentional, but the reader/compiler didn't complain, so it got me curious

seancorfield18:02:45

You can any expressions at the top-level in a file. They will all be evaluated in order when the file is loaded.

☝️ 2
seancorfield18:02:53

You shouldn't have top-level forms that have side-effects however -- such as opening files, creating connections to things, making HTTP calls etc. Put those in functions (or at least in a delay if you use a def - but functions are better practice).

seancorfield18:02:41

So a top-level string will be evaluated when the file is loaded and it has no side-effects so it is "harmless" but does cause code to be generated.

Ivan Quirino18:02:50

great explanation @U04V70XH6 thank you

vlad_poh02:02:58

@U04V70XH6 can you explain the “delay” bit? I have them def’ed but still see things kicking off or log output when I start my repl

seancorfield02:02:38

(def thing (delay (some-side-effecting :expression)))
...
(defn some-fn [args]
  ... @thing ...)
☝️:skin-tone-2: Then the side-effecting stuff will only run the first time your defer thing

👍 2
Sam Ritchie22:02:32

If you don’t like the ;; in some block you COULD also write #_”my string With newlines Etc”

Sam Ritchie22:02:56

Or (comment “…”)

Sam Ritchie22:02:02

But yes just use comments!

hiredman18:02:09

use comments

2
2