aboutsummaryrefslogtreecommitdiff
path: root/documentation/book/the_lux_programming_language/chapter_16.md
diff options
context:
space:
mode:
Diffstat (limited to 'documentation/book/the_lux_programming_language/chapter_16.md')
-rw-r--r--documentation/book/the_lux_programming_language/chapter_16.md93
1 files changed, 54 insertions, 39 deletions
diff --git a/documentation/book/the_lux_programming_language/chapter_16.md b/documentation/book/the_lux_programming_language/chapter_16.md
index 0040876d0..78c46fcf5 100644
--- a/documentation/book/the_lux_programming_language/chapter_16.md
+++ b/documentation/book/the_lux_programming_language/chapter_16.md
@@ -17,36 +17,42 @@ The `library/lux/test` module contains the machinery you need to write unit-test
Not only that, but the _Aedifex_ build tool for Lux also includes a command for testing: `lux test`
How do you set that up?
+
Let's take a look at the `project.lux` file for the Lux standard library itself.
```clojure
-{#identity ["com.github.luxlang" "stdlib" "0.6.0"]
+[""
+ ["identity" ["com.github.luxlang" "stdlib" "0.6.0-SNAPSHOT"]
+
+ "deploy_repositories" ["snapshots" "https://oss.sonatype.org/content/repositories/snapshots/"
+ "releases" "https://oss.sonatype.org/service/local/staging/deploy/maven2/"]
-#deploy_repositories {"snapshots" "https://oss.sonatype.org/content/repositories/snapshots/"
- "releases" "https://oss.sonatype.org/service/local/staging/deploy/maven2/"}
+ "repositories" ["https://oss.sonatype.org/content/repositories/snapshots/"
+ "https://oss.sonatype.org/service/local/staging/deploy/maven2/"]]
-#repositories ["https://oss.sonatype.org/content/repositories/snapshots/"
- "https://oss.sonatype.org/service/local/staging/deploy/maven2/"]
+ "jvm"
+ ["compiler" ["com.github.luxlang" "lux-jvm" "0.6.0-SNAPSHOT" "jar"]]
-#compiler ["com.github.luxlang" "lux-jvm" "0.6.0" "jar"]
-#description "Standard library for the Lux programming language."
-#test "test/lux"}
+ "bibliotheca"
+ ["info" ["description" "Standard Library for the Lux programming language."]
+ "test" "test/lux"]
+ ]
```
-The `#test` parameter is similar to the `#program` parameter in that it specifies the name of a Lux module.
+The `"test"` parameter specifies the name of a Lux module that serves as the entry point for testing.
Here is a summary of the file:
```clojure
-(.module:
- [library
- ["/" lux #*
- [program (#+ program:)]
- ["_" test (#+ Test)]
- [control
- ["." io]]
- ...
- ]])
+(.using
+ [library
+ ["/" lux "*"
+ [program {"+" program:}]
+ ["_" test {"+" Test}]
+ [control
+ ["[0]" io]]
+ ...
+ ]])
(program: args
(io.io (_.run! (_.times 100 ..test))))
@@ -54,41 +60,45 @@ Here is a summary of the file:
```
A test suit consists of a `Test` (or a composite of as many `Test`s as you want), which is then `run!`.
+
The `times` combinator allows you to execute `Test`s several times.
+
This can be very useful when using random data generation within your tests, as each run of the tests will lead to the generation of different sorts of data.
+
This will help you cover many possible scenarios within the same test run, and perhaps uncover tricky corner cases you wouldn't have thought of.
But where do those tests come from?
+
Nothing is being defined here.
Let's take a look at the tests defined in a simpler module.
-Well, the run macro, from lux/test pulls in all the tests from the imported modules to run them later once the program starts.
+Well, the `run` macro, from `library/lux/test` pulls in all the tests from the imported modules to run them later once the program starts.
To know how tests work, let's take a look at one of those modules.
From `test/lux/data/collection/stack`.
```clojure
-(.module:
+(.using
[library
- [lux #*
- ["_" test (#+ Test)]
+ [lux "*"
+ ["_" test {"+" Test}]
[abstract
- [monad (#+ do)]
+ [monad {"+" do}]
[\\specification
- ["$." equivalence]
- ["$." functor (#+ Injection)]]]
+ ["$[0]" equivalence]
+ ["$[0]" functor {"+" Injection}]]]
[control
- ["." maybe]]
+ ["[0]" maybe]]
[data
- ["." bit ("#\." equivalence)]]
+ ["[0]" bit ("[1]#[0]" equivalence)]]
[math
- ["." random]
+ ["[0]" random]
[number
["n" nat]]]]]
[\\library
- ["." /]])
+ ["[0]" /]])
(def: (injection value)
(Injection /.Stack)
@@ -99,7 +109,7 @@ To know how tests work, let's take a look at one of those modules.
(<| (_.covering /._)
(_.for [/.Stack])
(do random.monad
- [size (\ random.monad map (n.% 100) random.nat)
+ [size (# random.monad map (n.% 100) random.nat)
sample (random.stack size random.nat)
expected_top random.nat]
($_ _.and
@@ -111,33 +121,33 @@ To know how tests work, let's take a look at one of those modules.
(_.cover [/.size]
(n.= size (/.size sample)))
(_.cover [/.empty?]
- (bit\= (n.= 0 (/.size sample))
+ (bit#= (n.= 0 (/.size sample))
(/.empty? sample)))
(_.cover [/.empty]
(/.empty? /.empty))
(_.cover [/.value]
(case (/.value sample)
- #.None
+ {.#None}
(/.empty? sample)
- (#.Some _)
+ {.#Some _}
(not (/.empty? sample))))
(_.cover [/.next]
(case (/.next sample)
- #.None
+ {.#None}
(/.empty? sample)
- (#.Some [top remaining])
- (\ (/.equivalence n.equivalence) =
+ {.#Some [top remaining]}
+ (# (/.equivalence n.equivalence) =
sample
(/.top top remaining))))
(_.cover [/.top]
(case (/.next (/.top expected_top sample))
- (#.Some [actual_top actual_sample])
+ {.#Some [actual_top actual_sample]}
(and (same? expected_top actual_top)
(same? sample actual_sample))
- #.None
+ {.#None}
false))
))))
@@ -146,11 +156,15 @@ To know how tests work, let's take a look at one of those modules.
There's a lot going on here.
First of all, by using the `covering` macro, you can tell the test suit to track the coverage that your test suite has of a given module.
-That way, if your tests miss some _exported_, the report you'll get after running the tests will tell you, so you can judiciously choose to either expand your coverage, or skip covering them.
+
+That way, if your tests miss some _exported/public_ definitions, the report you'll get after running the tests will tell you, so you can judiciously choose to either expand your coverage, or skip covering them.
+
The `for` and `cover` macros then signal whenever one or more definitions are being covered by a given test.
Lux also defines some _specifications_, which are basically parameterizable tests, which implement consistent testing for various interfaces in the standard library.
+
That way, when testing an implementation of those interfaces, instead of having to copy-paste, or re-invent the testing every time, the specification is imported.
+
This enables consistent testing of implementations.
`and` allows you to _sequentially_ compose `Test`s into a larger `Test`.
@@ -160,6 +174,7 @@ You can also see an example of how to use randomness to generate sample data for
---
If you want to learn more about how to write tests, feel free to check out the test-suite for the Lux standard library.
+
It's very comprehensive and filled with good examples.
---