Skip to content

Commit

Permalink
Test setup and teardown (#122)
Browse files Browse the repository at this point in the history
  • Loading branch information
tengstrand authored Aug 28, 2021
1 parent c92acd5 commit 4b08954
Show file tree
Hide file tree
Showing 30 changed files with 246 additions and 56 deletions.
6 changes: 3 additions & 3 deletions components/deps/src/polylith/clj/core/deps/lib_deps.clj
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@
(or tag old-tag) (assoc :git/tag (or tag old-tag))
root (assoc :deps/root root))
(throw (Exception. (str "Unknown library type: " type))))
(seq exclusions)
(assoc :exclusions (vec exclusions))))
(seq exclusions)
(assoc :exclusions (vec exclusions))))

(defn key-as-symbol
"The library names (keys) are stored as strings in the workspace
Expand All @@ -46,5 +46,5 @@
(defn resolve-deps [project is-verbose]
"Resolves which library versions that are used by the given project."
(let [config (->config project)
_ (when is-verbose (println (str "# config=" config)))]
_ (when is-verbose (println (str "# config:\n" config) "\n"))]
(tools-deps/resolve-deps config {})))
48 changes: 37 additions & 11 deletions components/test-runner/src/polylith/clj/core/test_runner/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,26 @@
(when (contains? (set projects-to-test) project-name)
(map :namespace (:test namespaces))))

(defn run-test-statements [project-name class-loader test-statements run-message color-mode]
(defn execute-fn [function fn-type project-name class-loader color-mode]
(when function
(println (str "Running test " fn-type " for the " (color/project project-name color-mode)
" project: " function))
(try
(when (= :missing-fn
(common/eval-in class-loader
`(if-let [~'fun (clojure.core/requiring-resolve '~function)]
(~'fun '~project-name)
:missing-fn)))
(println (color/error color-mode (str "Could not find " fn-type " function: " function))))
(println)
(catch Throwable t
(let [message (str (or (some-> t .getCause) (.getMessage t)))]
(println (color/error color-mode (str "\nTest " fn-type " failed: " message))))))))

(defn run-test-statements [project-name class-loader test-statements run-message is-verbose color-mode]
(println (str run-message))
(when is-verbose (println (str "# test-statements:\n" test-statements) "\n"))

(doseq [statement test-statements]
(let [{:keys [error fail pass]}
(try
Expand Down Expand Up @@ -77,22 +95,25 @@
(defn run-tests-for-project [{:keys [bases components] :as workspace}
{:keys [name paths namespaces] :as project}
{:keys [project-to-bricks-to-test project-to-projects-to-test]}
is-verbose]
{:keys [setup-fn teardown-fn]}
is-verbose color-mode]
(when (-> paths :test empty? not)
(let [color-mode (-> workspace :settings :color-mode)
lib-paths (resolve-deps project is-verbose color-mode)
(let [lib-paths (resolve-deps project is-verbose color-mode)
all-paths (set (concat (:src paths) (:test paths) lib-paths))
bricks (concat components bases)
bricks-to-test (project-to-bricks-to-test name)
projects-to-test (project-to-projects-to-test name)
run-message (run-message name components bases bricks-to-test projects-to-test color-mode)
test-namespaces (brick-test-namespaces bricks bricks-to-test)
brick-test-namespaces (brick-test-namespaces bricks bricks-to-test)
project-test-namespaces (project-test-namespaces name projects-to-test namespaces)
test-statements (map ->test-statement (concat test-namespaces project-test-namespaces))
test-statements (mapv ->test-statement (concat brick-test-namespaces project-test-namespaces))
class-loader (common/create-class-loader all-paths color-mode)]
(when is-verbose (println (str "# paths:\n" all-paths "\n")))
(if (-> test-statements empty?)
(println (str "No tests to run for the " (color/project name color-mode) " project."))
(run-test-statements name class-loader test-statements run-message color-mode)))))
(do (execute-fn setup-fn "setup" name class-loader color-mode)
(run-test-statements name class-loader test-statements run-message is-verbose color-mode)
(execute-fn teardown-fn "teardown" name class-loader color-mode))))))

(defn has-tests-to-run? [{:keys [name]} {:keys [project-to-bricks-to-test project-to-projects-to-test]}]
(not (empty? (concat (project-to-bricks-to-test name)
Expand All @@ -107,6 +128,10 @@
projects-to-test))]
(println (str "Projects to run tests from: " projects))))

(defn print-execution-time [start-time]
(println)
(time-util/print-execution-time start-time))

(defn print-bricks-to-test [component-names base-names bricks-to-test color-mode]
(when bricks-to-test
(let [components-to-test (sort (set/intersection component-names (set bricks-to-test)))
Expand All @@ -116,7 +141,7 @@
bricks (str/join ", " (concat components bases))]
(println (str "Bricks to run tests for: " bricks)))))

(defn run [{:keys [components bases projects changes messages] :as workspace} is-verbose color-mode]
(defn run [{:keys [components bases projects changes settings messages] :as workspace} is-verbose color-mode]
(if (validator/has-errors? messages)
(validator/print-messages workspace)
(let [start-time (time-util/current-time)
Expand All @@ -130,6 +155,7 @@
(print-projects-to-test projects-to-test color-mode)
(print-bricks-to-test component-names base-names bricks-to-test color-mode)
(println)
(doseq [project projects-to-test]
(run-tests-for-project workspace project changes is-verbose))))
(time-util/print-execution-time start-time))))
(doseq [{:keys [name] :as project} projects-to-test]
(let [test-settings (get-in settings [:projects name :test])]
(run-tests-for-project workspace project changes test-settings is-verbose color-mode)))))
(print-execution-time start-time))))
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

(def major 0)
(def minor 2)
(def patch 0)
(def revision "alpha11")
(def patch 12)
(def revision "alpha")
(def name (str major "." minor "." patch "-" revision))

(def date "2021-08-08")
(def date "2021-08-28")

(defn version
([ws-type]
Expand Down
2 changes: 1 addition & 1 deletion doc/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ poly help
```

```
Poly 0.2.0-alpha11 (2021-08-08) - https://github.com/polyfy/polylith
Poly 0.2.0-alpha11.issue-110.2 (2021-08-28) - https://github.com/polyfy/polylith
poly CMD [ARGS] - where CMD [ARGS] are:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
(ns project.command-line.test-setup
(:require [clojure.test :refer :all]))

(defn test-setup [project-name]
(println (str "--- test setup for " project-name " ---")))

(defn test-teardown [project-name]
(println (str "--- test teardown for " project-name " ---")))
5 changes: 3 additions & 2 deletions examples/doc-example/workspace.edn
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{:top-namespace "se.example"
:interface-ns "interface"
:default-profile-name "default"
:add-to-git-when-create true
:compact-views #{}
:vcs {:name "git"
:auto-add true}
:release-tag-pattern "v[0-9]*"
:stable-tag-pattern "stable-*"
:projects {"development" {:alias "dev"}
"command-line" {:alias "cl"}
"command-line" {:alias "cl"
:test {:setup-fn project.command-line.test-setup/test-setup
:teardown-fn project.command-line.test-setup/test-teardown}}
"user-service" {:alias "user-s"}}}
Binary file modified images/profile-info-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified images/profile-test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
127 changes: 111 additions & 16 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -1750,6 +1750,7 @@ Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Test results: 1 passes, 0 failures, 0 errors.

Execution time: 1 seconds
```

Expand Down Expand Up @@ -1922,6 +1923,7 @@ Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Test results: 1 passes, 0 failures, 0 errors.

Execution time: 1 seconds
```

Expand Down Expand Up @@ -2029,11 +2031,93 @@ Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Test results: 1 passes, 0 failures, 0 errors.

Execution time: 3 seconds
```

Looks like it worked!

### Test setup and teardown

Sometimes we need to perform some test setup/teardown before and after we execute the tests for a project.

If any code is used by more than one project, we can put it in a separate component,
but in this case we should put it in the `command-line` project's `test` directory
because it's not used by any other project.

Let's create a `test-setup` namespace in the project's test directory and add two functions to it:
```
example
├── projects
│ └── command-line
│ └── test
│ └── project
│ └──command_line
│ └──test_setup.clj
```
```
(ns project.command-line.test-setup
(:require [clojure.test :refer :all]))

(defn test-setup [project-name]
(println (str "--- test setup for " project-name " ---")))

(defn test-teardown [project-name]
(println (str "--- test teardown for " project-name " ---")))
```
We need to keep two things in mind:
- Make sure the source code which contains our function, is accessible from the project
it's executed from (the `command-line` project in this case). Here the project's own `test` directory
was already added earlier by the `create project` command, so we are fine.
- Make sure the functions take exactly one parameter, the project name.

We also need to specify the two functions in `workspace.edn`:
```
...
:projects {"development" {:alias "dev"}
"command-line" {:alias "cl"
:test {:setup-fn project.command-line.test-setup/setup
:teardown-fn project.command-line.test-setup/teardown}}}}
```

If we don't need the tear down function, we can leave it out.

Let's run our tests:
```
poly test
```
```
Projects to run tests from: command-line

Running test setup for the command-line project: project.command-line.test-setup/test-setup
--- test setup for command-line ---

Running tests from the command-line project, including 2 bricks: user, cli

Testing se.example.cli.core-test

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Test results: 1 passes, 0 failures, 0 errors.

Testing se.example.user.interface-test

Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Test results: 1 passes, 0 failures, 0 errors.

Running test teardown for the command-line project: project.command-line.test-setup/test-teardown
--- test teardown for command-line ---

Execution time: 1 seconds
```

Nice, it worked!

### Summary

Let's summarize the different ways to run the tests.
The brick tests are executed from all projects they belong to except for the development project
(if `:dev` is not passed in):
Expand Down Expand Up @@ -2066,8 +2150,10 @@ by giving a list of bricks. This can be specified in `workspace.edn`, e.g.:
```
{...
:projects {"development" {:alias "dev", :test []}
"command-line" {:alias "cl", :test ["cli"]}}}

"command-line" {:alias "cl",
:test {:include ["cli]
:setup-fn se.example.test-helper.interface/setup
:teardown-fn se.example.test-helper.interface/teardown}}}}
```
...or by using this syntax:
```
Expand All @@ -2093,37 +2179,44 @@ they will never be tested from that project even if we pass in `:all`.
Let's start with the development project. The main purpose of this project is to allow us to work with our
code from an IDE using a single REPL. When doing that, the project must be set up in a way that
it's 100% compatible with tool.deps and the IDE integration. This is also the reason we have to
add the test paths explicitly in `./deps.edn` and often also the `src` and `resources` paths
so that the IDE integration will work in all environments.
it 100% compatible with tool.deps and the IDE integration. This is also the reason we have to
add the test paths explicitly in `./deps.edn`, which gives us access to the tests from the REPL.
To give us access to the `src` and `resources` paths from the REPL, we often add them as `:extra-paths`
because we want to make sure that the IDE integration will work in all the development
environments on the market.
> Note: At the time of writing, adding bricks to `development` using the `:local/root` syntax works fine in
> VSCode/Calva and Emacs/CIDER, but unfortunately not in IDEA/Cursive, see [this](https://github.com/cursive-ide/cursive/issues/2554) issue.
> However, if your organisation doesn't use Cursive, it should be fine to use the `:local/root` syntax even for the development project.
The `./deps.edn` config file sets up all our paths and dependencies,
and when we include the `dev` and `test` aliases (and sometimes `profile` aliases, described in the next section)
we instruct tools.deps what source code and libraries should be accessible from our IDE and REPL.
we inform tools.deps what source code and libraries should be accessible from our IDE and REPL.
When this is set up correctly, we are also able to run our tests from the REPL,
which will have access to all `test` and `src` code. Libraries that are defined in the `src`
context will therefore automatically be accessible when running the tests. Additional libraries that are
only used from the tests should be defined in the `test` context.
But we can also run tests using `poly test`, `clojure -M:poly test`, or `clojure -M:poly test`.
When we run the `test` command, the tool will detect what component, bases and projects that have been
When we run the `test` command, the tool will detect which components, bases and projects have been
affected since the last stable point in time. Based on this information, it will go through all
the affected projects, one at a time, and run the component, base and project tests that are included in each project.
That set of tests will be executed in isolation from its own class loader
which will speed up the test execution but also make it more reliable. Libraries from both the `src` and `test` context
the affected projects, one at a time, and run the component, base, and project tests that are included in each project.
This set of tests will be executed in isolation from its own class loader
which will speed up the test execution and make it more reliable. Libraries from both the `src` and `test` context
(and libraries that they depend on) will be used when the tests are executed.
If `:verbose` is given when running the tests, the libraries and paths that are being used will be printed out.
The development project can also be used to run tests, but that's not its main purpose.
The libraries to use in each project when running the `poly test` command is the sum of all library dependencies that are defined in all the
components and bases (either indirectly via `local/root` or directly by using `:deps`/`extra-deps`).
If a library is defined more than once in the set of bricks and projects, then the latest version of
that library will be used, if not overridden by `:override-deps`.
that library will be used, if not overridden by `:override-deps` in the project.
At the project level (except for the development project) we only need to define the libraries that are not defined in the included bricks,
At the project level we only need to define the libraries that are not defined in the included bricks (specified by its `:deps` key)
which can be libraries like clojure itself, `org.clojure/clojure`, that we don't want to repeat in all our bricks.
Finally, if we have a brick like `datomic-ions`, we can specify a repository it needs, like [this](examples/local-dep/deps.edn).
We can verify that the repo is picked up by the brick by executing `poly ws get:components:datomic-ions:maven-reops`:
Finally, if we have a brick like `datomic-ions`, we can specify which repository it needs, like [this](examples/local-dep/deps.edn).
We can verify that the repo is picked up by the brick by executing `poly ws get:components:datomic-ions:maven-repos`:
```
{"datomic-cloud" {:url "s3://datomic-releases-1fc2183a/maven/releases"}}
```
Expand Down Expand Up @@ -3001,7 +3094,9 @@ poly ws get:settings
:base-names [],
:project-names []}},
:projects {"development" {:alias "dev"},
"command-line" {:alias "cl"},
"command-line" {:alias "cl"
:test {:setup-fn project.command-line.test-setup/test-setup,
:teardown-fn project.command-line.test-setup/test-teardown},
"user-service" {:alias "user-s"}},
:tag-patterns {:stable "stable-*", :release "v[0-9]*"},
:thousand-separator ",",
Expand Down
9 changes: 8 additions & 1 deletion scripts/create-example.sh
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ cd ..
tree example > $output/project-tree.txt
cd example
cp $sections/project/deps.edn .
cp $sections/project/workspace.edn .
cp $sections/project/workspace1.edn ./workspace.edn
cp $sections/project/command-line-deps.edn projects/command-line/deps.edn

echo "### 6/50 Build ###"
Expand Down Expand Up @@ -181,11 +181,18 @@ poly info :all-bricks fake-sha:e7ebe68 > $output/testing-info-7.txt
poly info :all-bricks :dev fake-sha:e7ebe68 > $output/testing-info-8.txt
poly info :all fake-sha:e7ebe68 > $output/testing-info-9.txt
poly info :all :dev fake-sha:e7ebe68 > $output/testing-info-10.txt

cp $sections/testing/workspace.edn .
poly info :all :dev fake-sha:e7ebe68 > $output/testing-info-11.txt
poly test :all :dev > $output/testing-test-all.txt
sed -i '' -E "s/Execution time: [0-9]+/Execution time: x/g" $output/testing-test-all.txt

cp $sections/project/command-line-test-setup.clj projects/command-line/test/project/command_line/test_setup.clj
cp $sections/project/workspace2.edn ./workspace.edn

cp $sections/project/command-line-test-setup.clj projects/command-line/test/project/command_line/test_setup.clj
cp $sections/project/workspace2.edn ./workspace.edn

echo "### 11/50 Profile ###"
echo "current-dir=$(pwd)"
cp $sections/profile/workspace.edn .
Expand Down
2 changes: 1 addition & 1 deletion scripts/output/help/01-help.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Poly 0.2.0-alpha11 (2021-08-08) - https://github.com/polyfy/polylith
Poly 0.2.0-alpha11.issue-110.2 (2021-08-28) - https://github.com/polyfy/polylith

poly CMD [ARGS] - where CMD [ARGS] are:

Expand Down
1 change: 1 addition & 0 deletions scripts/output/local-dep-old-format/test-migrated.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,5 @@ Ran 1 tests containing 1 assertions.
0 failures, 0 errors.

Test results: 1 passes, 0 failures, 0 errors.

Execution time: x seconds
1 change: 1 addition & 0 deletions scripts/output/local-dep-old-format/test.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@

Execution time: x seconds
Loading

0 comments on commit 4b08954

Please sign in to comment.