You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-[Guide: How to Create a Custom Assertion][create-a-custom-assertion]
17
+
11
18
## Motivation
12
19
20
+
> "_Another_ assertion library? Are you daft? My test framework has its own assertions!"
21
+
>
22
+
> ‒sickos, probably
23
+
13
24
Look, I'm ~~old~~~~wizened~~~~experienced~~ knowledegable and I've written a lot of tests. I've used a lot of assertion libraries. There are ones I prefer and ones I don't.
14
25
15
26
But none of them do quite what _this_ does. The main goals of this library are:
@@ -20,15 +31,15 @@ But none of them do quite what _this_ does. The main goals of this library are:
20
31
21
32
A chainable API may provide type safety. But it seems to _guarantee_ implementing a custom assertion will be complicated. The API surface is necessarily a combinatoric explosion of methods.
22
33
23
-
> [!WARNING]
34
+
> ⚠️ **Caution!**
24
35
>
25
-
> Because chainable APIs are familiar, you may hate _BUPKIS_ once you see some examples. You don't have to use it, but please: _don't confuse familiarity with usability_.
36
+
> Because chainable APIs are familiar, you may hate _BUPKIS_ once you see some examples. Nobody's making you use it. But please, keep an open mind & give me this grace: _don't confuse familiarity with usability_.
26
37
27
-
To achieve these goals, I've adopted the following design principles:
38
+
To achieve these goals, _BUPKIS_ makes the following design choices.
28
39
29
-
### Natural-Language Assertions
40
+
### Assertions are Natural Language
30
41
31
-
In `bupkis` (stylized as "_BUPKIS_"), you **don't** write this:
42
+
When you're using _BUPKIS_, you **don't** write this:
32
43
33
44
```js
34
45
expect(actual).toEqual(expected);
@@ -60,99 +71,111 @@ Then _BUPKIS_ wants you to write:
60
71
61
72
```js
62
73
expect(actual, 'to be a string');
63
-
// it is tolerant of poor/ironic grammar
74
+
// it is tolerant of poor/ironic grammar, sometimes
64
75
expect(actual, 'to be an string');
65
76
```
66
77
67
-
Can't remember the string? Did you forget a word or make a typo? Maybe you also forgot _BUPKIS_ is type-safe? You'll get a nice squiggly for your trouble.
78
+
Can't remember the string? Did you forget a word or make a typo? Maybe you also forgot **_BUPKIS_ is type-safe?** You'll get a nice squiggly for your trouble. This isn't black magic. It ain't a _cauldron_. We're not just _throwing rat tails and `string`s in there._
79
+
80
+
> "Preposterous! Codswallop!"
81
+
>
82
+
> ‒the reader and/or more sickos
83
+
84
+
Right—how could this be anything by loosey-goosey _senselessness_? I beg to differ; _BUPKIS_ is nothing if not _intentional_.
85
+
86
+
The first parameter to a _BUPKIS_ assertion is always the _subject_ ([def.](https://bupkis.zip/documents/Reference.Glossary_of_Terms#subject)).
68
87
69
-
The "string" part of an expectation is known as a _phrase_. Every expectation will contain, at minimum, one phrase. As you can see from the above example, phrases often have aliases.
88
+
The "string" part of a _BUPKIS_ assertion is known as a _phrase_. Every expectation will contain_at minimum_ one (1) phrase. As you can see from the above "to be a string" example, phrases often have aliases.
70
89
71
-
You can negate just about any phrase:
90
+
Assertions may have multiple phrases or parameters, but the simplest assertions always look like this:
One more convention worth mentioning is _negation_.
103
+
104
+
You can _negate_ just about any phrase by prepending it with `not` and a space. For example:
72
105
73
106
```js
74
107
expect(actual, 'to be', expected);
75
-
// did they not teach grammar in nerd school??
76
-
expect(actual, 'not is', expected);
108
+
expect(actual, 'not to be', expected);
77
109
78
110
expect(
79
111
() =>thrownewTypeError('aww, shucks'),
80
-
'to throw a',
112
+
'not to throw a',
81
113
TypeError,
82
-
'not satisfying',
114
+
'satisfying',
83
115
/gol durn/,
84
116
);
85
117
```
86
118
87
-
### Custom Assertions
119
+
### Custom Assertions by Zod
88
120
89
-
In _BUPKIS_, custom assertions are _first-class citizens_. You can create your own assertions with minimal boilerplate. You don't have to learn a new API or a new DSL (maybe); you just use [Zod][]. _It's so easy, even a **archaic human** could do it!_
121
+
[Zod][] is a popular object validation library which does some heavy lifting for _BUPKIS_. In fact, its fundamentals get us _most_ of the way to a type-safe assertion library!
90
122
91
-
Read [Guide: How to Create a Custom Assertion](https://boneskull.github.io/bupkis/documents/Guides.How_to_Create_a_Custom_Assertion) to learn more.
123
+
So We recognized that many (most?) custom assertions can be _implemented as Zod schemas._
92
124
93
-
## Prerequisites
125
+
Here's a ~~stupid~~ ~~quick~~ _stupid_ example of a creating and "registering" a basic assertion _which can be invoked using two different phrases_:
94
126
95
-
_BUPKIS_ requires **Node.js ^20.19.0 || ^22.12.0 || >=23** and ships as a dual CJS/ESM package.
127
+
```ts
128
+
import { z, use, createAssertion } from'bupkis';
96
129
97
-
The library has been designed for Node.js environments and testing frameworks.
130
+
conststringAssertion=createAssertion(
131
+
z.string(),
132
+
[['to be based', 'to be bussin']],
133
+
z.string(),
134
+
);
98
135
99
-
## Installation
136
+
const { expect } =use([stringAssertion]);
100
137
101
-
```bash
102
-
npm install bupkis -D
138
+
expect('chat', 'to be based');
139
+
expect('fam', 'to be bussin');
140
+
141
+
// did you know? includes all builtin assertions!
142
+
expect('skiball lavatory', 'to be a string');
103
143
```
104
144
105
-
## Usage
145
+
**If you can express it in Zod, you can make it an assertion.** There's also a [function-based API][custom-assertion-function] for use with [parametric][] and behavioral assertions.
106
146
107
-
Here:
147
+
👉 For a thorough guide on creating assertions, read [Guide: How to Create a Custom Assertion][create-a-custom-assertion].
108
148
109
-
```ts
110
-
import { expect } from'bupkis';
111
-
112
-
// Basic type assertions
113
-
expect('hello', 'to be a string');
114
-
expect(42, 'to be a number');
115
-
expect(true, 'to be a boolean');
116
-
117
-
// Value comparisons
118
-
expect(10, 'to equal', 10);
119
-
expect('hello world', 'to contain', 'world');
120
-
expect([1, 2, 3], 'to have length', 3);
121
-
122
-
// Negation
123
-
expect(42, 'not to be a string');
124
-
expect('hello', 'not to equal', 'goodbye');
125
-
126
-
// Object assertions
127
-
const user = { name: 'Alice', age: 30 };
128
-
expect(user, 'to be an object');
129
-
expect(user, 'to have property', 'name');
130
-
expect(user, 'to satisfy', { name: 'Alice' });
131
-
```
149
+
## Prerequisites
132
150
133
-
For comprehensive documentation and guides, visit the [project documentation](https://boneskull.github.io/bupkis/).
151
+
_BUPKIS_ requires **Node.js ^20.19.0 || ^22.12.0 || >=23**. It has a peer dependency on [Zod][] v4+, but will install it as an optional dependency if you are not already using it.
134
152
135
-
### Worth Mentioning Right Now
153
+
_BUPKIS_ ships as a dual CJS/ESM package.
136
154
137
-
_BUPKIS_ has two main exports:
155
+
> Disclaimer: _BUPKIS_ has been designed to run on Node.js in a development environment. Anyone attempting to deploy _BUPKIS_ to some server somewhere will get what is coming to them.
138
156
139
-
-`expect()`: the main entrypoint for synchronous assertions
140
-
-`expectAsync()`: the main entrypoint for asynchronous assertions
157
+
## Installation
141
158
142
-
> [!IMPORTANT]
143
-
>
144
-
> As of this writing, the assertions available in `expectAsync()` are all `Promise`-related (and custom assertions can even use an async schema for the subject); they are completely disjoint from the assertions available in `expect()`. **This will likely change in the future.**
159
+
```bash
160
+
npminstallbupkis-D
161
+
```
145
162
146
-
## Project Scope
163
+
## Usage
164
+
165
+
👉 See the [Basic Usage Guide](https://bupkis.zip/documents/guides.basic_usage) for a quick introduction.
147
166
148
-
1. It's an assertion library
167
+
📖 Visit [https://bupkis.zip](https://bupkis.zip) for comprehensive guides and reference.
149
168
150
169
## Prior Art & Appreciation
151
170
152
171
- [Unexpected][] is the main inspiration for _BUPKIS_. However, creating types for this library is exceedingly difficult (and was in fact the first thing I tried). Despite that drawback, I find it more usable than any other assertion library I've tried.
153
172
- [Zod][] is a popular object validation library which does most of the heavy lifting for _BUPKIS_. It's not an assertion library, but there's enough overlap in its use case that it makes sense to leverage it.
154
173
- [fast-check][]: A big thanks to Nicholas Dubien for this library. There is **no better library** for an assertion library to use to test itself! Well, besides itself, I mean. How about _in addition to_ itself? Yes. Thank you!
155
174
175
+
## Why is it called _BUPKIS_?
176
+
177
+
TODO: think of good reason and fill in later
178
+
156
179
## A Note From The Author
157
180
158
181
> _"This is my assertion library. Many are like it, but this one is mine."_
@@ -164,5 +187,11 @@ _BUPKIS_ has two main exports:
0 commit comments