Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
tutorials:beginner:testing [2019/07/10 16:10] – [Writing our own test for the beginner tutorial code] gkazhoya | tutorials:beginner:testing [2022/03/15 10:12] (current) – [Integrating into the ASDF testing operation] schimpf | ||
---|---|---|---|
Line 1: | Line 1: | ||
====== Writing tests ====== | ====== Writing tests ====== | ||
- | (Caution: this tutorial is currently under construction. Please come back tomorrow to see the complete version.) | ||
- | **Description: | + | **Description: |
**Previous Tutorial:** [[tutorials: | **Previous Tutorial:** [[tutorials: | ||
Line 9: | Line 8: | ||
===== lisp-unit ===== | ===== lisp-unit ===== | ||
+ | The library we are going to use to write our unit tests is called '' | ||
+ | |||
+ | For more information on the library check it's [[https:// | ||
+ | |||
+ | |||
+ | ===== Setting up the infrastructure and our first test ===== | ||
+ | |||
+ | Let's create a new ASDF system for our test suite, it should be in the root of our ROS package, right next to '' | ||
<code lisp> | <code lisp> | ||
- | blabla | + | (defsystem cram-my-beginner-tutorial-tests |
+ | :depends-on (cram-my-beginner-tutorial | ||
+ | | ||
</ | </ | ||
+ | In addition to creating a new ASDF system, we are going to create a new directory within our ROS package, called '' | ||
+ | We need a '' | ||
- | ===== Writing our own test for the beginner tutorial code ===== | + | <code lisp> |
+ | (defpackage :cram-my-beginner-tutorial-tests | ||
+ | (:nicknames : | ||
+ | (:use : | ||
+ | </code> | ||
- | We are going to write some tests for the functions | + | In this tutorial we are going to test the function '' |
+ | Therefore, | ||
+ | **Name | ||
+ | So, let's create '' | ||
- | Let's create a new test asdf system | + | <code lisp> |
+ | (in-package :tut-tests) | ||
- | Now let us load the test package | + | ;;; Turtle world is like this: |
+ | ;;; Y | ||
+ | ;;; ^ | ||
+ | ;;; | | ||
+ | ;;; | | ||
+ | ;;; o-----> X | ||
+ | ;;; Identity orientation is looking right in the direction of X axis. | ||
+ | |||
+ | (define-test relative-angle-to-dont-turn | ||
+ | ;; turtle is in origin with identity orientation, | ||
+ | (let* ((goal (cl-transforms: | ||
+ | | ||
+ | | ||
+ | (assert-number-equal angle 0.0))) | ||
+ | </ | ||
+ | |||
+ | So, we are testing the '' | ||
+ | in order to look towards a goal point. | ||
+ | |||
+ | What does the test checks the following situation: | ||
+ | if the turtle is standing in the origin of the coordinate frame of the Turtlesim world, | ||
+ | looking in the identity orientation, | ||
+ | what would be the angle with which it would have to turn to look at coordinate '' | ||
+ | As '' | ||
+ | |||
+ | Let us add just another test case: if the turtle is standing at the origin with identity orientation | ||
+ | and wants to look at '' | ||
+ | Let us create a new test function for this, such that your '' | ||
<code lisp> | <code lisp> | ||
- | TUT> (ros-load:load-system " | + | (in-package : |
- | TUT> | + | |
+ | ;;; Turtle world is like this: | ||
+ | ;;; Y | ||
+ | ;;; ^ | ||
+ | ;;; | | ||
+ | ;;; | | ||
+ | ;;; o-----> X | ||
+ | ;;; Identity orientation is looking right in the direction of X axis. | ||
+ | |||
+ | (define-test relative-angle-to-dont-turn | ||
+ | ;; turtle is in origin with identity orientation, | ||
+ | (let* ((goal (cl-transforms:make-3d-vector 3 0 0)) | ||
+ | | ||
+ | | ||
+ | (assert-number-equal angle 0.0))) | ||
+ | |||
+ | (define-test relative-angle-to-look-back | ||
+ | ;; turtle is in origin, wants to look at (-1 0 0) | ||
+ | (let* ((goal (cl-transforms:make-3d-vector -1 0 0)) | ||
+ | | ||
+ | | ||
+ | (assert-number-equal angle pi))) | ||
</ | </ | ||
+ | You can see that we needed to use the '' | ||
+ | So let us add these to the '' | ||
- | Let's see how to implement | + | <code lisp> |
+ | (defsystem cram-my-beginner-tutorial-tests | ||
+ | :depends-on (cram-my-beginner-tutorial | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | :components ((:module " | ||
+ | : | ||
+ | ((:file " | ||
+ | | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Running the test suite ===== | ||
+ | |||
+ | Now let us load the test package in REPL and switch into its namespace. | ||
+ | You might need to restart your Emacs at this point, because the new ASDF system might not be visible to Emacs without restarting. | ||
<code lisp> | <code lisp> | ||
- | (define-test ... | + | CL-USER> |
+ | CL-USER> (in-package :tut-tests) | ||
</ | </ | ||
+ | To run the tests written with the '' | ||
+ | <code lisp> | ||
+ | TUT-TESTS> | ||
+ | Unit Test Summary | ||
+ | | 2 assertions total | ||
+ | | 2 passed | ||
+ | | 0 failed | ||
+ | | 0 execution errors | ||
+ | | 0 missing tests | ||
+ | |||
+ | #< | ||
+ | </ | ||
+ | |||
+ | We can also specify which test specifically we want to run: | ||
+ | |||
+ | <code lisp> | ||
+ | TUT-TESTS> | ||
+ | Unit Test Summary | ||
+ | | 1 assertions total | ||
+ | | 1 passed | ||
+ | | 0 failed | ||
+ | | 0 execution errors | ||
+ | | 0 missing tests | ||
+ | |||
+ | #< | ||
+ | </ | ||
+ | |||
+ | If we are in a different package in the REPL than the test suite package, we can also specify the package to test explicitly: | ||
+ | |||
+ | <code lisp> | ||
+ | TUT-TESTS> | ||
+ | #< | ||
+ | CL-USER> (lisp-unit: | ||
+ | Unit Test Summary | ||
+ | ... | ||
+ | </ | ||
+ | |||
+ | ==== Integrating into the ASDF testing operation ==== | ||
+ | |||
+ | As there exist multiple testing libraries in Lisp, the ASDF facility has a unified way of calling the test suites, independent of the library. | ||
+ | So let us integrate our test suite with the ASDF interface. | ||
+ | For that, we are going to add a '': | ||
+ | |||
+ | <code lisp> | ||
+ | (defsystem cram-my-beginner-tutorial-tests | ||
+ | :depends-on (cram-my-beginner-tutorial | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | :components ((:module " | ||
+ | :components | ||
+ | ((:file " | ||
+ | | ||
+ | :perform (test-op (operation component) | ||
+ | (symbol-call :lisp-unit '#: | ||
+ | </ | ||
+ | |||
+ | And to use the ASDF interface for testing, we simply need to call the '' | ||
+ | We can do that at any point in the REPL. So let us start a completely fresh REPL with nothing loaded in it and try the testing function: | ||
+ | |||
+ | <code lisp> | ||
+ | CL-USER> (asdf: | ||
+ | T | ||
+ | </ | ||
+ | |||
+ | You will get some compilation prompts in between and in the end it should return T. | ||
+ | Now let us destroy one of our tests and see what happens: change the angle in the first test in '' | ||
+ | |||
+ | <code lisp> | ||
+ | (define-test relative-angle-to-dont-turn | ||
+ | ;; turtle is in origin with identity orientation, | ||
+ | (let* ((goal (cl-transforms: | ||
+ | | ||
+ | | ||
+ | (assert-number-equal angle pi))) | ||
+ | </ | ||
+ | |||
+ | Now let us rerun the tests: | ||
+ | |||
+ | <code lisp> | ||
+ | CL-USER> (asdf: | ||
+ | |||
+ | Unit Test Summary | ||
+ | | 2 assertions total | ||
+ | | 1 passed | ||
+ | | 1 failed | ||
+ | | 0 execution errors | ||
+ | | 0 missing tests | ||
+ | |||
+ | T | ||
+ | </ | ||
+ | |||
+ | To get more detailed information, | ||
+ | |||
+ | <code lisp> | ||
+ | CL-USER> (setf lisp-unit: | ||
+ | T | ||
+ | CL-USER> (asdf: | ||
+ | | Failed Form: PI | ||
+ | | Expected 0.0d0 but saw 3.141592653589793d0 | ||
+ | | | ||
+ | RELATIVE-ANGLE-TO-DONT-TURN: | ||
+ | |||
+ | RELATIVE-ANGLE-TO-LOOK-BACK: | ||
+ | |||
+ | Unit Test Summary | ||
+ | | 2 assertions total | ||
+ | | 1 passed | ||
+ | | 1 failed | ||
+ | | 0 execution errors | ||
+ | | 0 missing tests | ||
+ | |||
+ | #< | ||
+ | </ | ||
+ | |||
+ | Now go back and fix the angle to the correct value and see if the tests pass again. | ||
+ | |||
+ | |||
+ | ===== Adding some more assertions ===== | ||
+ | |||
+ | Now let us add more tests for the '' | ||
+ | Let's add some more tests for the '' | ||
+ | In addition to that assertion, there are also many different types of assertions, so we are going to try the '' | ||
+ | Put the following into your '' | ||
+ | |||
+ | <code lisp> | ||
+ | (in-package :tut-tests) | ||
+ | |||
+ | ;;; Turtle world is like this: | ||
+ | ;;; Y | ||
+ | ;;; ^ | ||
+ | ;;; | | ||
+ | ;;; | | ||
+ | ;;; o-----> X | ||
+ | ;;; Identity orientation is looking right in the direction of X axis. | ||
+ | |||
+ | (define-test relative-angle-to-dont-turn | ||
+ | ;; turtle is in origin with identity orientation, | ||
+ | (let* ((goal (cl-transforms: | ||
+ | | ||
+ | | ||
+ | (assert-number-equal angle 0.0)) | ||
+ | |||
+ | ;; turtle is in (3 0 0) with identity orientation, | ||
+ | (let* ((goal (cl-transforms: | ||
+ | | ||
+ | :x 3.0 :y 0.0 :theta 0.0)) | ||
+ | | ||
+ | (assert-number-equal angle 0.0)) | ||
+ | |||
+ | ;; turtle is in (0 0 0) looking up in direction of Y, wants to look at (0 1 0) | ||
+ | (let* ((goal (cl-transforms: | ||
+ | | ||
+ | :x 0.0 :y 0.0 :theta (/ pi 2))) | ||
+ | | ||
+ | (assert-number-equal angle 0.0)) | ||
+ | |||
+ | ;; turtle is in (1 1 0) looking towards the origin, wants to look at (0 0 0) | ||
+ | (let* ((goal (cl-transforms: | ||
+ | | ||
+ | :x 1.0 :y 1.0 :theta (+ pi (/ pi 4)))) | ||
+ | | ||
+ | (assert-number-equal angle 0.0))) | ||
+ | |||
+ | (define-test relative-angle-to-look-back | ||
+ | ;; turtle is in origin, wants to look at (-1 0 0) | ||
+ | (let* ((goal (cl-transforms: | ||
+ | | ||
+ | | ||
+ | (assert-number-equal angle pi)) | ||
+ | |||
+ | ;; turtle is in (1 0 0), wants to look at origin | ||
+ | (let* ((goal (cl-transforms: | ||
+ | | ||
+ | :x 1.0 :y 0.0 :theta 0.0)) | ||
+ | | ||
+ | (assert-number-equal angle pi)) | ||
+ | |||
+ | ;; turtle is in (-1 -1 0) looking towards origin, wants to look away from origin | ||
+ | (let* ((goal (cl-transforms: | ||
+ | | ||
+ | :x -1.0 :y -1.0 :theta (/ pi 4))) | ||
+ | | ||
+ | (assert-number-equal angle pi))) | ||
+ | |||
+ | (define-test relative-angle-to-type-error | ||
+ | (assert-error ' | ||
+ | (tut:: | ||
+ | | ||
+ | | ||
+ | </ | ||
+ | At this point, all the tests should be successful. | ||
+ | Please beware that if you define a test with a specific name, then delete it from the file but not restart the REPL, the test will still be in the current REPL session. So if you delete a test from a file, also think about restarting your REPL. |