risei v3.3.1
Risei Read-Me
Overview
Risei is a new way to write unit tests that's easier and faster, more dependable, and keeps your tests from standing in the way of redesigns.
Risei does all this by replacing hand-coded tests with simple declarative syntax.
- Risei has many convenient and time-saving features.
- Risei works with object-oriented JavaScript in modules.
- Risei works with TypeScript after just a few tweaks.
You can find a longer version of this read-me at https://deusware.com/risei. It expands greatly on the information here.
Examples
Here are two example tests, in which SortModel.countSort()
is being tested using the inputs from .in
and the expected output from .out
:
Here are the example test results for those two tests:
- Outputs are always listed, even on passing tests, which makes code usage much clearer.
- Any failing tests appear in light red.
- Your latest tests always sort to the bottom so it's easy to find their results.
Test runs have a title bar so the starting point is easy to find:
And they have a summary bar at the bottom:
- This bar is red when any tests fail, much as you'd expect.
Status
Risei's major features are now complete, but new enhancements or fixes may appear from time to time. The latest release, 3.3.1, fixes an edge case with addressing properties.
Check out the full list of changes.
Features of Risei
Declarative syntax to write tests simply and quickly. ► Writing tests (basics below)
Easy-to-read test definitions and test outputs. ► Test and output examples (also above)
Even less to write by stating reused test properties only once. ► Collapsing forward (basics below)
Built-in declarative syntax to fake test-time values from dependencies. ► Spoofing using
.plus
(basics below)Full support for
async
code with no special syntax needed. ► Writing tests (basics below)Testing properties and methods, static and instance members all the same way. ► Properties and statics
Testing
throw
paths effortlessly. ► Using.and: "throws"
Deriving actual values to test from raw outputs or property retrieval. ► Using
.from
Setting up and tearing down arbitrary test state. ► Using
.do
and.undo
Testing for
undefined
as output. ► Usingthis.undef
Running a method repeatedly in one test. ► Using
.and: "poly"
And more! Check out the full Risei home page.
Installation
Install Risei for development time only:
npm install --save-dev risei
Ensure that package.json
specifies ECMAScript modules:
"type": "module"
And add Risei's metadata to package.json
:
"risei": {
"tests": "**.rt.js"
}
Testing Risei Itself
To test Risei itself, you clone it from a parallel repository, install its dependencies, and run its self-tests. See the full explanation here.
Writing Tests
You write tests in .rt.js
files like this:
import ATestSource from "risei/ATestSource";
import ClassToTest from "ClassToTest.js";
export class SomeTests extends ATestSource {
tests = [ ... ];
}
Write individual tests as plain JavaScript objects with Risei's simple syntax:
tests = [ ...
{ on: ContainerClass, with: [ "a", "b", "c" ], // Target class and constructor args.
of: "doesContain", // Target method name.
for: "When the arg is present, returns true.", // Description of test.
in: [ "c" ], // Inputs to method.
out: true }, // Expected output.
... ];
- Asynchronous code with
async
andawait
keywords can tested with no changes at all to this syntax. - Use empty arrays for
.in
or.with
when there are no args to pass. - You can use long names for properties if you want.
Running Tests
Once you have some tests written, you can run them manually:
node ./node_modules/risei/index.js
Or write a script in package.json
that does the same:
"scripts": {
"test": "node ./node_modules/risei/index.js"
}
And then run that script:
npm test
Basic collapsing forward example
You can state repeated test properties just once, and let them collapse forward across subsequent tests to save time and make tests easier to read.
Risei collapses values together from partial or full test objects until it has a full test to run. Every property collapses forward until it is changed or wiped out:
{ on: ContainerClass, with: [ "a", "b", "c" ] }, // Following tests are of this class.
{ of: "doesContain" }, // Following tests are of this method.
{ for: "Returns true when arg present.", in: [ "c" ], out: true }, // First test: now Risei has enough to run on.
{ for: "Returns false when arg absent.", in: [ "d" ], out: false }, // Next test: same method, different test case.
{ of: "countOf" }, // Change of tested method. Method-related props are wiped out.
...
{ on: SortingClass, with: [ ] }, // Change of tested class. All existing props are wiped out.
- There are more options available, and an exclusion for mutated args.
- Learn all the details here.
Basic spoofing example
You can use declarative spoofing syntax to define what dependencies of your targeted code return for it to use.
The most basic spoofing looks like this:
{
on: TestedClass,
...
plus: [
{ on: Dependency, of: "someMethod", as: 10 }, // Dependency.someMethod() returns 10 in this test.
{ of: "testedClassMethod", as: 11 } // TestedClass.testedClassMethod() returns 11 in this test.
],
...
}
- It's just like fakes, mocks, or test doubles in other test systems, but easier to write and read.
- Numerous additional capabilities, as well as compressed syntax, can be mixed freely in many ways.
- Learn more here.
TypeScript with Risei
To test TypeScript code with Risei, you make sure the code is transpiled before the tests are run, and you point Risei to the transpiled .js
files.
- Learn all about it here.
Troubleshooting
If errors are thrown while testing, gold bars listing them appear at the bottom, and full stack traces appear amid the test output. If problems cause test files not to load, a gold bar with the error message also appears:
- If files don't load, tests in those files disappear from the output and the totals.
- Those and other problems can be solved with the help of this troubleshooting guide.
Version history
- Release 3.3.1 (February, 2025) contains this change:
- You can now spoof and otherwise address value properties (formally data descriptors) that don't have an initial value, whether they are static or instance class members.
- Release 3.3.0 (January, 2025) adds this change to those of other recent releases:
- You can now test instance members with the same names as static members using a new
.and
option of"instance"
.
- You can now test instance members with the same names as static members using a new
- Release 3.2.1 (January, 2025) contains all the changes from 3.2.0, 3.1.1, and 3.1.0, plus this change:
- Risei's mistaken nominal dependency on npm has been removed.
- Release 3.2.0 (January, 2025) contains all the changes from 3.1.1 and 3.1.0, plus this change:
- Risei's self-dependency for its own self-testing has been moved back to the development-only scope.
- Release 3.1.1 (January, 2025) contains these changes:
- Extra commas and non-objects in arrays of test objects are disregarded, and no longer cause a throw.
- Throws in wider scopes are now listed at the end, like others already found there.
- Release 3.1.0 (January, 2025) contains these changes:
- Accessor properties (formally accessor descriptors) are now compared for test results and displayed in test outputs.
- Any accessor properties that throw errors during display are displayed as
(threw)
.
- Any accessor properties that throw errors during display are displayed as
- Throws in tested code and in test framing code are now listed at the end, and also displayed amid the tests.
File
objects now have a succinct custom display in outputs.- (Breaking change) In
constructor
tests, constructed instances are available in.from
functions only asactual
/test.actual
.- (Breaking change) In
constructor
tests,test.target
is now theprototype
of the tested class.
- (Breaking change) In
- Widespread internal reengineering of other kinds.
- Accessor properties (formally accessor descriptors) are now compared for test results and displayed in test outputs.
Older releases are dropped from this list progressively over time. Using the latest release is recommended.
Known issues and workarounds
There are the known minor issues:
- If args for a test are mutated by tested code, the mutated args are used when collapsing forward.
- The workaround is just to restate those args for each test.
- Spoofing accessor properties only works when they have both a getter and a setter.
- The workaround is to find another way to produce the property values you need.
- Predefined JavaScript methods like
toString()
may not be recognized, nor any instance method that has the same name as a static method.- The workaround is to use an
.and
value of"instance"
for any methods like these that you test.
- The workaround is to use an
Exclusions from Risei
At present, there are a few things Risei doesn't support. Some of these may be supported in the future.
- You can see the whole list here.
Risei can be run alongside other test frameworks, so if you can't test all of your code with Risei, you can still save a lot of time by using Risei to test the bulk of it.
Maker
Risei is written by myself, Ed Fallin. I'm a longtime software developer who likes to find better ways to do things.
If you find Risei useful, consider spreading the word to other devs, making a donation, suggesting enhancements, or proposing sponsorships.
You can get in touch about Risei at riseimaker@gmail.com.
License
Risei is published for use under the terms of the MIT license:
Risei Copyright © 2023–2025 Ed Fallin
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
5 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
11 months ago
11 months ago
11 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago