Coder Social home page Coder Social logo

bond's Introduction

Bond CircleCI Status

Bond is a spying and stubbing library, primarily intended for tests.

[circleci/bond "0.6.0"]
(ns test.foo
  (:require [bond.james :as bond :refer [with-spy]]))

(defn foo [x]
  (let [shaken (with-out-str (prn :martini))]
    [shaken])

(defn bar [y]
  (foo y))

(deftest foo-is-called
  (with-spy [foo]
    (bar 2)
    (is (= 1 (-> foo bond/calls count)))))

Bond provides one main macro, with-spy. It takes a vector of defn vars (vars that resolve to fns). Each var will be redefined for the scope of the macro, wrapping the function to track arguments and call counts. At any point during the scope, you can call (bond/calls f), where f is a spied fn. calls returns a seq of maps, one for each call to f. Each map contains the keys :args, a seq of args the fn was called with, and one of :return or :throw.

Bond also provides with-stub!. It works the same as with-spy, but redefines the function to return (constantly nil) (default), while also spying on it. This is generally preferable to Clojure's built-in with-redefs macro since it will throw an exception if the mocked function is called with the wrong number of arguments. You can specify an arbitrary function instead of the default (constantly nil) by providing a [fn-var replacement-fn] vector in place of just the fn name:

(ns test.foo
  (:require [bond.james :as bond :refer [with-stub!]]))

(defn foo [x] ...)

(defn bar [y] ...)

(deftest foo-is-called
  (with-stub! [[foo (fn [x] "foo")]
               [bar (fn [y] "bar")]]
    (is (= ["foo" "bar"] [(foo 1) (bar 2)]))))

(deftest consecutive-stubbing
  (with-stub! [[foo [(fn [x] "foo1")
                     (fn [x] "foo2")
                     (fn [x] "foo3")]]
               [bar (fn [y] "bar")]]
    (is (= ["foo1" "foo2" "foo3" "bar"] [(foo 1) (foo 1) (foo 1) (bar 2)]))))

Private functions can also be stubbed or spyed:

(ns test.foo)

(defn- foo [x] ...)
(ns test.bar
  (:require [bond.james :as bond :refer [with-stub!]]
            [test.foo :as foo]))

(deftest foo-is-called
  (with-stub! [[foo/foo (fn [x] "foo")]]
    (is (= "foo" (#'foo/foo 1)))
    (is (= [1] (-> #'foo/foo bond/calls first :args)))))

There is also a with-stub macro which works like with-stub! but omits the argument check.

In addition to with-spy and with-stub!, Bond also provides with-spy-ns and with-stub-ns which can spy/stub every function in a namespace in one go:

(ns test.foo
  (:require [bond.james :as bond]
            [clojure.test :refer (deftest is)]))

(defn foo [] :foo)

(defn bar [] :bar)

(deftest you-can-stub-entire-ns
  (is (= :foo (foo)))
  (is (= :bar (bar)))
  (bond/with-stub-ns [[foo (constantly :baz)]]
    (is (= :baz (foo)))
    (is (= :baz (bar)))))

Releasing

New git tags are automatically published to clojars.

The following should be updated on the main branch before tagging:

  • project.clj - version
  • README.md - dependency coordinates
  • CHANGELOG.md - summary of changes

License

Distributed under the Eclipse Public License.

bond's People

Contributors

andrewwelton avatar appplemac avatar arohner avatar aterweele avatar atodd-circleci avatar bostonaholic avatar cldwalker avatar conormcd avatar dcarley avatar glenjamin avatar gordonsyme avatar justinc474 avatar liamchzh avatar machellerogden avatar marcomorain avatar neeasade avatar nivl avatar nwjsmith avatar renovate[bot] avatar sachinpkale avatar smaant avatar stig avatar technomancy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

bond's Issues

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.


Warning

Renovate failed to look up the following dependencies: Failed to look up clojure package org.clojure:clojure.

Files affected: project.clj


This repository currently has no open or pending branches.

Detected dependencies

circleci
.circleci/config.yml
  • cljkondo/clj-kondo 2024.08.01
leiningen
project.clj
  • org.clojure:clojure 1.10.2
  • lambdaisland:kaocha 1.91.1392
  • lambdaisland:kaocha-cloverage 1.1.89
  • lambdaisland:kaocha-junit-xml 1.17.101
  • lein-cloverage:lein-cloverage 1.2.4
  • jonase:eastwood 1.4.3

  • Check this box to trigger a request for Renovate to run again on this repository

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Error type: Cannot find preset's package (local>circleci/renovate-config:backplane.json5)

Preserve spec instrumentation when stubbing

Spec instrumentation as implemented by spec itself and orchestra both work by altering the var root to wrap the original function with extra behaviour that checks the spec registry and raises errors.

Because bond also replaces the var when doing stubbing, this instrumentation gets lost.

I think it would be useful if spec instrumentation was maintained during stubbing, to ensure that any stubbed behaviour adheres to the spec of the real implementation.

One suggestion from @ericfode was that rather than teach bond about spec instrumentation, we could teach the instrumentation about bond. Bond itself would then need some fairly generic code to pick up metadata and apply additional transformations after stubbing.

What do people think?

`with-spy` doesn't seem to work on tests with `:inline` definitions in metadata

As far as I can tell, calling with-spy (and with-stub!) seem to not work as expected on functions that have an inline definition (i.e. a function with :inline defined in the metadata).

(t/deftest a-test 
  (t/is (= 1 (count (bond/with-spy [inc]
                     (inc 1)
                     (bond/calls inc))))))
;; => #'user/a-test
user> (a-test)
​
FAIL in (a-test) (x.clj:226)
expected: (= 1 (count (bond/with-spy [inc] (inc 1) (bond/calls inc))))
  actual: (not (= 1 0))
;; => nil

(t/deftest a-test 
  (t/is (= 1 (count (bond/with-spy [identical?]
                     (identical? 1 2)
                     (bond/calls identical?))))))
;; => #'user/a-test
user> (a-test)
​
FAIL in (a-test) (x.clj:226)
expected: (= 1 (count (bond/with-spy [identical?] (identical? 1 2) (bond/calls identical?))))
  actual: (not (= 1 0))
;; => nil

;; NOTE: `=` is only inlined for its 2 argument form

(t/deftest a-test 
  (t/is (= 1 (count (bond/with-spy [=]
                     (= 1 2)
                     (bond/calls =))))))
;; => #'user/a-test
user> (a-test)
​
FAIL in (a-test) (x.clj:226)
expected: (= 1 (count (bond/with-spy [=] (= 1 2) (bond/calls =)))
  actual: (not (= 1 0))
;; => nil

;; Passes for non-inlined form
(t/deftest a-test 
  (t/is (= 1 (count (bond/with-spy [=]
                     (= 1 2 3)
                     (bond/calls =))))))
;; => #'user/a-test
user> (a-test)
;; => nil

Asking for permission VS asking for forgiveness

Hello former CircleCI colleagues! 🙋‍♂️

I've recently been working with python a lot, and I was sorely missing the functionality provided by this library. So I just reimplemented it in python. I hope that's okay?

See here: https://github.com/epgui/pybond

Just wanted to flag it here as a courtesy, and wanted to say that in case this caused any issues I would happily delete the library. But I hope I don't have to! I am also happy to add any text or extra attribution if applicable.

Typo in README

The sentence

"calls returns a seq of maps, one for each call to fb."

in the README contains the character 'b' which should be omitted. I can open a quick PR to fix this if needed.

Trouble compiling when requiring bond in cljs test ns

When requiring and compiling I receive the following exception:
...
Caused by: clojure.lang.ExceptionInfo: Assert failed: Argument to ns-publics must be a quoted symbol
(core/and (seq? quoted-ns) (= (first quoted-ns) (quote quote)) (core/symbol? (second quoted-ns))) at line 34 target/out/bond/james.cljc {:file "target/out/bond/james.cljc", :line 34, :column 8, :tag :cljs/analysis-error}
...

Building a local version of bond with a change to line 34 in ns->fn-symbols fixes the problem:
Change (ns-publics ns) to (ns-publics (quote ns)).

I'm relatively new to cljs so am not sure if something else, like project.clj config, is my actual problem. Also, I'm guessing the changed line would need to check for the existence of a quote before quoting if this were to prevent creating new problems.

Thanks

Thread-safe version of `with-spy` and `with-stub`

Bond uses with-redefs which binds the function's var's root to the new definition, which means it is visible to all threads that are currently executing, not necessarily the one in the context of with-spy or with-stub, for example

(defn funk [& args] args)

(future
  (bond/with-stub! [[funk (constantly -1)]]
    (Thread/sleep 10000)
    (funk)))

(funk 9) ;; => -1 (redefined function being called outside of with-stub)

;; after ten seconds
(funk 9) ;; => (9)

I was curious if you think using thread-local redefinitions is something you think would be useful here. For example, using https://gist.github.com/gfredericks/7143494

Don't blow up when arglists metadata is missing

Hey y'all 👋

I recently encountered a problem with bond whilst trying to mock functions generated by HugSQL. I opened an issue on that tool too, because I think those functions should be correctly annotated with :arglists.

However, I also think that throwing is not the correct behaviour in the absence of an :arglists annotation. Would it be possible to have a generated warning, or something more benign than a fail? Alternatively, maybe a different error message which highlights the missing :arglists rather than claiming that any number of arguments is the wrong number of arguments?

I'll happily do up a PR if I get some guidance on your preferred solution.

License

I was about to publish a fork of bond that includes more helper functions and such, since as discussed the other week your intention is to keep bond as short and simple as it is today :)

However, I realised there's no license on bond at the moment? Would you be able to declare one?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.