clarity-lang / reference

The Clarity Reference
146 stars 34 forks source link

Support for test code in contracts #58

Open jbencin opened 1 year ago

jbencin commented 1 year ago

It would be useful to allow for sections of code which are only interpreted in a testing environment, such as clarinet test. This would allow us to run unit tests on private functions, and with access to private variables, without resorting to hacks like using shell scripts to insert test code into the contract.

This could work something like Rust's #[cfg(test)], but following Clarity's existing conventions and syntax. I've been told by @lgalabru that clarinet check already allows customizing behavior using the syntax ;; #[statement], so that might be the best way to go. Here's a simple example from pox-3.clar of how this might be used:

;; `handle-unlock` is a private function that is called directly from the Stacks block processor, and not from inside the contract
;; To test it, we need to give it a public interface
;; #[test]
(define-public (test-handle-unlock (user principal) (amount-locked uint) (cycle-to-unlock uint))
    (handle-unlock user amount-locked cycle-to-unlock)
)
friedger commented 1 year ago

A comment should not reduce permission, instead something like this might work better:

;; `handle-unlock` is a private function that is called directly from the Stacks block processor, and not from inside the contract
;; To test it, we need to give it a public interface
;; #[public-for-test]
(define-private (test-handle-unlock (user principal) (amount-locked uint) (cycle-to-unlock uint))
    (handle-unlock user amount-locked cycle-to-unlock)
)
jbencin commented 1 year ago

A comment should not reduce permission

I agree with this statement, but then you lose the ability to tell whether the code is used in production or only for testing. I think that's useful information to have, as you may want your tools to treat test code differently (like having a minifier or obfuscator strip it out, or configuring clarinet check to ignore warnings in it).

Maybe this shouldn't be done with comments. Here's an alternate proposal:

(?test
    ;; Test code block. Can define multiple functions and variables here
    (define-public (test-handle-unlock (user principal) (amount-locked uint) (cycle-to-unlock uint))
        (handle-unlock user amount-locked cycle-to-unlock)
    )
)