Skip to content

Latest commit

 

History

History
128 lines (92 loc) · 3.93 KB

README.md

File metadata and controls

128 lines (92 loc) · 3.93 KB

smsio

Sending SMS messages via HTTP with Twilio

This app demonstrates how a state can be just a funciton as well as how states can be swapped for testing.

Sending Texts

In order to send SMS, you would need to create/add your Twilio creds to the smsio config

After that is done, texting is pretty simple.

Boot smsio with Boot

Change directory root to smsio, and:

[smsio]$ boot repl
boot.user=> (dev)
#object[clojure.lang.Namespace 0x53810ad6 "dev"]
dev=> (reset)
INFO  utils.logging - >> starting..  #'app.conf/config
INFO  utils.logging - >> starting..  #'app.sms/send-sms
INFO  utils.logging - >> starting..  #'app.www/web-server
{:started ["#'app.conf/config" "#'app.sms/send-sms" "#'app.www/web-server"]}
dev=>

Send HTTP POST to smsio

The easiest way is to use curl. The format is:

;; as compojure reads it..
(POST "/sms/:from/:to/:msg")

(replace the phone numbers with your Twilio and recipient ones):

curl -X POST "http://localhost:4242/sms/+15104266868/+17180000000/mount%20is%20fun%20:)"

and...

I am sure you noticed, but the Twilio phone number this SMS is sent from is:

+ 1 (510) 42 MOUNT :)

Swapping SMS sender for testing

The reason for this example is to show how to test by swapping components with their mocks / stubs.

Every app is different, and states to swap will also be different. We are going to replace a send-sms state that is defined in the app as:

(defn create-sms-sender [{:keys [sid auth-token]}]
  (fn [{:keys [from to body]}]
    (twilio/with-auth sid auth-token
      (twilio/send-sms 
        (twilio/sms from to body)))))

(defstate send-sms :start (create-sms-sender 
                            (:sms config)))

notice that, once started, the send-sms will be just a funciton. Which means that if it is needed to be replaced during testing, it can be replaced with a test function that, for example, receives an SMS and puts in on a core.async channel:

(fn [sms] 
  (go (>! sms-ch sms)))

One thing to note, the real twilio/send-sms returns a future, so in order to be (if needed) as close as possble to the "real thing", we'll also return a future:

(fn [sms] 
  (go (>! sms-ch sms))
  (future))

Swapping Realities

Now all that needs to be done is to create a test function and let mount know to use it instead if the real one.

;; ...
(let [sms-ch (chan)
      send-sms (fn [sms]
                 (go (>! sms-ch sms))
                 (future))]                        ;; twilio API returns a future
  (mount/start-with {#'app.sms/send-sms send-sms})
;; ...

Putting this theory to the real test:

(deftest swapping-with-value
  (testing "sms endpoint should send sms"
    (let [sms-ch (chan)
          send-sms (fn [sms]
                     (go (>! sms-ch sms))
                     (future))]                        ;; twilio API returns a future
      (mount/start-with {#'app.sms/send-sms send-sms})
      (http/post (post-sms-url "mars" "earth" "we found a bottle of scotch!"))
      (is (= "we found a bottle of scotch!"
             (:body (<!! sms-ch)))))
    (mount/stop)))

In this test all the application components work with a test send-sms function rather than the real one. In other (more OO) words, mount injected a test send-sms into an app instead of the real one.

Check out the working test to get a visual on how all the above pieces come together.

Running tests is easy:

[smsio]$ boot test