Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

:var does not seem to work with dynamic vars #127

Open
Fuco1 opened this issue Jul 23, 2018 · 3 comments · May be fixed by #128
Open

:var does not seem to work with dynamic vars #127

Fuco1 opened this issue Jul 23, 2018 · 3 comments · May be fixed by #128

Comments

@Fuco1
Copy link
Contributor

Fuco1 commented Jul 23, 2018

I was trying to do something like this: for each spec set a dynamic variable to a test mock value. I can't use before-each and setq because that would modify the global state forever.

However, emacs allows let binding a dynamic variable and that should then work for all the code called in that scope (dynamic).

;; -*- lexical-binding: t -*-

(defvar my-dynamic-var 123)

(describe "Test dynamic vars by binding in describe-form"
  :var ((my-dynamic-var nil))

  (it "does rebind dynamic vars"
    (expect my-dynamic-var :to-be nil)))

(describe "Test dynamic vars by binding in it-form"

  (it "does rebind dynamic vars"
    (let ((my-dynamic-var nil))
      (expect my-dynamic-var :to-be nil))))

I would therefore expect this to work, but the test fails. I think the it form is called in a different "dynamic" environment than the describe. I'm not sure if this can be somehow reconsiled. A "work around" is to let bind the variable in the it forms but this leads to major duplication (I have 20-40 it forms for each describe)

Maybe there could be some other mechanism to do this, like an unwind-protect that would set and re-set the variables but this would need a different keyword or notation.


There's also the useful idiom to preserve a value of a special variable, for example kill-ring like so

(let ((kill-ring kill-ring)) ; bind var to itself
  (whatever-here))

The whatever-here code can do whatever but the global state of the kill-ring will be preserved when it returns. This is quite useful in tests also. This would also be possible if :var worked like this, i.e. preserved the "call stack" and dynamic lookup.

@Fuco1
Copy link
Contributor Author

Fuco1 commented Jul 23, 2018

I think the analysis is as follows: since buttercup relies on lexical scoping, the let form generated by :var is placed as parent to all the "gathering functions" (buttercup-before-each, buttercup-it etc) which generate lambdas which capture these variables in their context. Then this works fine because it's the same variable in all the closures and so before-each affects the it forms as expected.

But then the functions are simply run in a loop from somewhere else and the dynamic scope is no longer extended there. Yea, not quite sure what to do here, but a feature to modify dynamic scope would be very very rad.

@DarwinAwardWinner
Copy link
Contributor

In the meantime, it might be prudent to have :var throw an error if lexical binding is not enabled.

@snogge
Copy link
Collaborator

snogge commented Nov 5, 2019

Buttercup doesn't work without lexical binding, this is documented in two places in doc/writing-tests.md. So a global error for disabled lexical binding seems more appropriate.
Warning for dynamic variables used in a :var could be useful though.

snogge added a commit to snogge/emacs-buttercup that referenced this issue Aug 14, 2022
Signal an error if the :Var or :var* keyword is found in any other
position than the first argument of a describe macro.  Fixes jorgenschaefer#223.

There are several issues with the :var(*) keywords, the most
important being it's inability to handle dynamic variables (jorgenschaefer#127).
But there is also the question of what the correct behaviour would be
if you use multiple :var(*) or when they are put anywhere but at the
beginning of the describe macro.
snogge added a commit to snogge/emacs-buttercup that referenced this issue Aug 14, 2022
Signal an error if the :var or :var* keyword is found in any other
position than the first argument of a describe macro.  Fixes jorgenschaefer#223.

There are several issues with the :var(*) keywords, the most
important being it's inability to handle dynamic variables (jorgenschaefer#127).
But there is also the question of what the correct behaviour would be
if you use multiple :var(*) or when they are put anywhere but at the
beginning of the describe macro.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants