TABLE OF CONTENTS


Test-driven development works well with the bottom-up style of coding that is natural in Scheme and other Lisps. As you work in the Visual Scheme for ApplicationsTM (“VSA®”) REPL to develop your custom solutions, you will eventually find it necessary to simplify setting up specific test scenarios by writing scripts and/or tests that you can run on demand. These can be run either at the command line prompt for regression testing, or for recreating state on restarting the REPL. 


Although not yet a complete testing solution, the initial release of the (visualscheme unit-testing) library now included with AcceλerateTM for Microsoft 365 provides basic support for unit-level testing that you can leverage to improve your development productivity. Over time we plan to add more capabilities such as a built-in test harness and other convenience features; but as of now you can begin to incorporate unit testing into your development process.



(visualscheme unit-testing)


This library can also be used by importing (unit-testing) but this is not recommended as we intend to deprecate that convenience library, left over from earlier versions of our unit test framework. All it does is import symbols from (visualscheme unit-testing) and re-export them. Many of our early tests derived from popular Scheme books are the only reason it’s still around.



Form Name:  check


Basics


Signature

(check that <s-expression> 

            from <context-string> 

            is [numerically | exactly] {equal | equivalentto <expected-result> 

            because <explanation>)

Exported From

(visualscheme unit-testing)

Type of Form

Syntax extension

Description

This form allows you to define a unit-level test whose success/failure results are collected with other tests that you may have defined in the current test run. The syntax reads like natural English and is designed to produce readable results, particularly in the case of test failure when calling (report-results) at the end of a test run.


Formal Parameters


Parameter

Description

<s-expression>

Any valid Scheme S-expression that returns a value or other testable result.

<context-string>

A meaningful description of the context of the test. This is intended to make error reports more meaningful.

<expected-result>

The value expected from the <expression> tested.

<explanation>

A description of the expected result or otherwise rationale for the test.


Examples


(define foo 5)
(
define the-first-simple-example "the first simple example")
(
check that foo
from the-first-simple-example
       is numerically equal to 
5      ;; see remarks below
       because 
"foo should equal 5.")

=> Testing: foo should equal 
5 => PASS

(
set! foo 4)
(
check that foo
from the-first-simple-example
       is numerically equal to 
5
       because 
"foo should equal 5")


=> Testing: foo should equal 
5 => FAIL


> (
report-results)

------------------------------------------
-- Final Results
------------------------------------------

Passed: 
1
Failed: 
1

Failed tests:

the first simple example: foo should equal 5 =>  Expected 5, but got 4 instead.



Remarks

The (check …) form exposes four kinds of Scheme equality that you can use in your unit testing to ensure correct comparisons are performed:


  1. Generic equality, i.e. equal?, via the “is equal to” clause

  2. Numerical equality, i.e.  =, via “is numerically equal to” clause

  3. “Exact” equality, i.e. eq?, via the “is exactly equal to” clause

  4. Equivalence, i.e. eqv?, via the “is equivalent to” clause



The rules for equality in the R6RS Scheme specification are somewhat complicated, and you are encouraged to become familiar with them. When in doubt, use the generic form for most tests, and numerical equality for numbers. 



Form Name: print-banner


Basics


Signature

(print-banner <string>)

Exported From

(visualscheme unit-testing)

Type of Form

Procedure

Description

This procedure allows you to precede one or more unit tests with an informative banner that will be displayed during test execution.


Formal Parameters


Parameter

Description

<string>

The string to be displayed in the banner.


Examples


> (print-banner "Database Tests")


------------------------------------------
-- Database Tests
------------------------------------------

>


Remarks

Use of this procedure is optional but as your unit tests grow, it can help to make the output more readable. You can also roll your own way to do this, of course.



Form Name:  report-results


Basics


Signature

(report-results)

Exported From

(visualscheme unit-testing)

Type of Form

Procedure

Description

This procedure prints the results of a unit test run. To be more precise: when invoked, it prints the current number of passed and failed tests, and for failed tests, concludes with the complete current list of failures with detail about what is expected versus actual results.


Formal Parameters


None.



Examples


> (report-results)


------------------------------------------
-- Final Results
------------------------------------------

Passed: 
1
Failed: 
1

Failed tests:

the first simple example: foo should equal 5 =>  Expected 5, but got 4 instead.


Remarks


This is a work in progress. Until we finalize the unit testing library’s ultimate test harness strategy, structuring a unit test run is currently left as an exercise for the user. What we typically do with the current capability is described here as a convention, but you can devise your own approach if you find our current practice inadequate for your needs. Please let us know what you come up with, and why you think it’s a better approach by submitting a Support Ticket via the Support Desk and choosing Feature Request/Feedback.


First, we create a top-level script to execute a test suite, which we (for now) put in the “\\scripts” subdirectory of a project root. By convention, we prefix the file name of such scripts with “test-”. Currently, we treat C:\Users\<your-account>\.accelerate as a central “nexus” or primary root working directory, where you can extend the search path to other locations on your harddrive, in your order of precedence/priority, depending on what you are working on and how different projects relate to each other. Whether you are working directly from that directory, or from some other directory that you added to your vsa.json file’s “searchPath” list, anything that you save in a “\\scripts” subdirectory of a root directory in the search path will be resolvable using (resolve-script “<script-relative-path>”).


Note: (resolve-script…) is from the (visualscheme path-utils) library that is automatically loaded on startup of both adb.exe and the Accelerate365.dll add-in. Functions in (visualscheme path-utils) are described elsewhere in this documentation under “Files, IO and Environment-Related Functionality.”


The following is an example of one the top-level scripts that you can use as a template:


(import (ironscheme)
        (
prefix (little-schemer) tls/)
        (
visualscheme unit-testing))

(
time  ;; when finished, display the total time for the full test run
 (
begin (tls/run-all-tests)
        (
report-results)))


What’s going on here is that we are running an entire library of unit tests from a central starting point. Within the scope of our unit test library we have defined several such toplevel scripts. In this case, we are testing various examples of chapters from The Little Schemer. The (little-schemer) library, to which are defining a “tls/” prefix for reference within the body of the script, contains a (run-all-tests) “entry point” for all the tests we defined from that book. The (run-all-tests) entry point looks like this in the lib\little-schemer.sls file:


  (define run-all-tests
    (
lambda ()
      (
run-chapter-one-tests)
      (
run-chapter-two-tests)
      (
run-chapter-three-tests)
      (
run-chapter-four-tests)
      (
run-chapter-five-tests)
      (
run-chapter-six-tests)
      (
run-chapter-seven-tests)
      (
run-chapter-eight-tests)
      (
run-chapter-nine-tests)
      (
run-chapter-ten-tests)))


Each of the functions called in (run-all-tests) are defined in that library, and themselves refer to other functions found in imported libraries from the same folder structure. In this way you can modularize your testing, and include only tests you know are ready for full-regression. You can also create more specialized toplevel-scripts for narrower subsets of tests that you may need to perform during a specific bug fix or test new feature you are adding to your code.


For additional assistance on this topic, please feel free to submit a Support Ticket via the Support Desk Portal and describe what you are trying to achieve. 



Form Name:  reset-test-results


Basics


Signature

(reset-test-results)

Exported From

(visualscheme unit-testing)

Type of Form

Procedure with side effects (resetting passed and failed counters to 0)

Description

Sets the internal counters for passed and failed tests back to zero.


Formal Parameters


None.


Examples


> (reset-test-results)
> (
report-results)


------------------------------------------
-- Final Results
------------------------------------------

Passed: 
0
Failed: 
0


Remarks

This is ordinarily a function you will only call while experimenting in the REPL. When using top-level scripts as suggested above, it is unnecessary and in fact unhelpful.



Form Name:  object->string


Basics


Signature

(object->string <object>)

Exported From

(visualscheme unit-testing)

Type of Form

Function

Description

Given an object, creates a text-printable representation that by default will be displayed on the current output port. Used internally to display an expression or result in a form conducive to error reporting.


Formal Parameters


Parameter

Description

<object>

The object to be represented in string form in the output.


Examples


> (import (unit-testing))
> (
object->string 5)
"5"
> (
object->string (list))
"()"
> (
object->string (make-vector 3))
"#(() () ())"
>


Remarks


This function is not generally intended for external use, and is provided because it should be available to code that calls the (check …) macro. But it is offered in the event you find it useful.



Form Name:  try


Basics


Signature

(try <thunk>)

Exported From

(visualscheme unit-testing)

Type of Form

Function

Description

The (try …) form allows you to execute a valid Scheme expression with basic exception handling. This is provided to allow tests to run even if one or more of them throws an exception, but you may find it generally useful.


Formal Parameters


Parameter

Description

<thunk>

A valid Scheme expression that might throw an exception.


Examples


> (try (lambda () (/ 1 0)))
Testing: Condition components:
   
1. &assertion
   
2. &who: /
   
3. &message: "divide by zero"
   
4. &irritants: (1 0)
"Exception during execution (see inline above)"
>


Remarks


(try …) is the simplest possible exception handling that might work, and it’s fine for the way we execute unit tests. You may want to base a more complete approach to handling exceptions based on it. We use the (display-condition …) form from the (wak trc-testing display-condition) library, which will give you some idea of the macro magic lurking under the hood of even this simple form.



Other Forms


There are several other forms exported by the (visualscheme unit-testing) library. They are exported because the callers of (check …) need them available after macro expansion, but they are not intended to be used directly.




The following terms are registered trademarks of the Microsoft group of companies and are used in accordance with Microsoft’s Trade and Brand Guidelines: Microsoft, Microsoft 365, Microsoft Office, Microsoft Excel, Microsoft Edge, Microsoft Edge WebView2, Microsoft Windows, Excel, Office 365


The following terms are registered trademarks of Apex Data Solutions: Visual Scheme, VSA.


Copyright © 2022.  Apex Data Solutions, LLC. All Rights Reserved.