# TIL: Enforcing contracts with \`contract-out\` from within an embedded test module

**URL:** <https://racket.discourse.group/t/til-enforcing-contracts-with-contract-out-from-within-an-embedded-test-module/525>\
**Category:** Show & Tell\
**Tags:** contract, testing, module, submod\
**Created:** [January 4, 2022, 10:34pm UTC](https://racket.discourse.group/t/til-enforcing-contracts-with-contract-out-from-within-an-embedded-test-module/525 "2022-01-04T22:34:00Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![sschwarzer](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/sschwarzer/32/1940_2.png) [@sschwarzer](https://racket.discourse.group/u/sschwarzer)\
**Post date:** [January 4, 2022, 10:34pm UTC](https://racket.discourse.group/t/til-enforcing-contracts-with-contract-out-from-within-an-embedded-test-module/525/1 "2022-01-04T22:34:00Z")

</div>

# TL;DR 😉

It's possible to enforce and test contracts defined at the module boundary within a `test` module in the same file. Do this by `require`'ing `(submod "..")` in the `test` module:

```racket
(module+ test
  (require
    ...
    (submod ".."))

  ...)

```

Found at [Confused about racket contracts - Stack Overflow](https://stackoverflow.com/a/14718633/8854750)

* * *

# Discussion

Usually my modules are of the form

```scheme
#lang racket/base

(require racket/contract)

(provide
  (contract-out
    ...)
)

; Definitions
(define ...)

; Tests
(module+ test
  ...
)

```

Testing the module _mostly_ works fine, however the contracts defined with `contract-out` aren't applied in the tests. It would be possible to work around this by defining module contents with `define/contract` etc., but this has the disadvantage that the contracts are checked for all uses in the module which might slow down the code too much.

When I was looking for a way to test the contracts defined with `contract-out` from within the same file, I found the above StackOverflow answer. Due to the `(submod "..")` import in the `test` module it accesses the module in the same file across the file's module boundary.

---

<div class="post-metadata">

**Author:** ![sschwarzer](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/sschwarzer/32/1940_2.png) [@sschwarzer](https://racket.discourse.group/u/sschwarzer)\
**Post date:** [January 4, 2022, 10:40pm UTC](https://racket.discourse.group/t/til-enforcing-contracts-with-contract-out-from-within-an-embedded-test-module/525/2 "2022-01-04T22:40:28Z")

</div>

Another tip related to testing contracts: If a contract is relatively complicated and especially if it's difficult to test procedures where the contract is used, it may make sense to put the contract in its own predicate function and test this function.

For example, today I wrote this code:

```scheme
(define omit-fields/c
  (and/c (listof (or/c 'completed? 'priority 'completion-date 'creation-date))
         (lambda (omit-fields)
           ; These aren't actual dates, but they work the same way for
           ; `task-date-combination/c`.
           (define completion-date (index-of omit-fields 'completion-date))
           (define creation-date (index-of omit-fields 'creation-date))
           ; In the context of `task->string` we're interested in omitting the
           ; fields, i.e. _not_ having them.
           (task-date-combination/c (not completion-date) (not creation-date)))))

```

(the concrete background isn't relevant here), and I test `omit-fields/c` on its own in the `tests` module:

```scheme
  ...
  (run-tests
    (test-suite "Contracts"
      ; `task-date-combination/c`
      (test-case
        "Valid date combinations"
        (check-true (task-date-combination/c #f #f))
        (check-true (task-date-combination/c #f "2022-01-04"))
        (check-true (task-date-combination/c "2022-01-04" "2022-01-03")))
      (test-false
        "Completion date without creation date"
        (task-date-combination/c "2022-01-04" #f))
      ; `omit-fields/c`
      (test-false
        "Invalid task field symbol"
        (omit-fields/c '(priority invalid)))
      (test-true
        "Omit both dates"
        (omit-fields/c '(priority creation-date completion-date)))
      (test-true
        "Omit completion date, but keep creation date"
        (omit-fields/c '(priority completion-date)))
      (test-true
        "Omit neither date"
        (omit-fields/c '(priority)))
      (test-false
        "Omit creation date, but keep completion date"
        (omit-fields/c '(priority creation-date)))
  ))
  ...

```
