Security News
Node.js EOL Versions CVE Dubbed the "Worst CVE of the Year" by Security Experts
Critics call the Node.js EOL CVE a misuse of the system, sparking debate over CVE standards and the growing noise in vulnerability databases.
Bddly is a lightweight Behaviour Driven Development testing wrapper for Jest. Unlike other BDD frameworks, such as Cucumber, it doesn't require the use of non-code textual spec files. All your tests and specs are written in code, which makes for a more fluid workflow, and lets the compiler help you just like when writing the rest of your code.
Bddly generates nice html files with the output of your tests, which can be the living documentation of the behaviour of your system.
The code that generated this test output is pretty readable, and all in TypeScript:
spec('Order retrieval', async () => {
given.anOrderExistsInTheDatabaseWithName('My order');
await when.aRequestIsMadeForOrderByIndex(state.orderIndex);
then.theResponseTextIs('My order');
and.theResponseCodeIs(200);
});
Bddly was inspired by YatSpec, which is a BDD testing framework for Java.
Bddly is written in TypeScript, and is designed to work with TypeScript projects. Vanilla JavaScript projects may need some fiddling to get this to work!
npm i bddly --save-dev
In your tsconfig.json file, add "resolveJsonModule": true
so that Bddly can read your project name from package.json.
There is an example Nest.js project using Bddly that you might find instructive here. It has a slightly cleaner structure than the following example.
Bddly will look for test files under the bddly-tests folder. Your file name should end with -spec.ts
in order for it to be recognised.
Start by creating a standard Jest test file, then add the following import:
import { Step, spec, specFinished, suiteStart, suiteFinished } from '../bddly';
Your import path might be different, depending on where your test file is.
Bddly uses decorators to wrap your BDD test steps, so that it can capture information to generate reports. For this to work, we need a bit of boilerplate. Firstly, we need to create classes to hold the implementations of your BDD steps, so go ahead and create the following:
class Given {}
class When {}
class Then {}
class And {}
We're going to initialise these classes within the Jest beforeAll function, so at the top level let's declare them first:
let given: Given;
let when: When;
let then: Then;
let and: And;
Then in the beforeAll function:
given = new Given();
when = new When();
then = new Then();
and = new And();
suiteStart();
And for the final piece of boilerplate, add calls to the Jest afterEach and afterAll methods:
afterEach(() => {
specFinished(expect.getState().currentTestName);
});
afterAll(() => {
suiteFinished(expect.getState().currentTestName, __filename);
});
Now that's all set up, let's write our first test. We use the Bddly spec function to define our test, which takes a test name, and an anonymous function with the test steps.
Here's the whole call to Jest describe:
describe('Bddly test:', () => {
let given: Given;
let when: When;
let then: Then;
let and: And;
beforeAll(async () => {
given = new Given();
when = new When();
then = new Then();
and = new And();
suiteStart();
});
afterEach(() => {
specFinished(expect.getState().currentTestName);
});
afterAll(async () => {
suiteFinished(expect.getState().currentTestName, __filename);
});
describe('Example tests:', () => {
spec('Doubles a number', () => {
given.aNumber(2);
when.theNumberDoublerIsCalled();
then.theStoreShouldBe(4);
});
});
});
Of note, each describe description ends in a colon. Bddly uses this to know which of the describes are part of the path to the test, and which is the test name.
The last thing to do is to write the implementations of the BDD steps, within the Given, When and Then classes. We'll also create a variable to hold the number:
let store: number;
class Given {
@Step
aNumber(x: number) {
store = x;
}
}
class When {
@Step
theNumberDoublerIsCalled() {
store = store * 2;
}
}
class Then {
@Step
theStoreShouldBe(x: number) {
expect(store).toBe(x);
}
}
Just remember to annotate each of these methods with the @Step decorator, so that Bddly knows to pay attention to them!
And that's it. Now just...
Add a script to your package.json:
"test:e2e": "jest -i --config ./test/jest-e2e.json"
In this example, test-e2e.json is the jest config file. Your's might be different.
Then in the terminal run npm run test:e2e
.
You should see the result of your test in the terminal (hopefully it passes!), and there should be a new top level folder in your project called bdd-reports. Open index.html in your browser to see a contents list of your Bddly tests.
Real world apps have dependencies you need to include in your test. For instance, a database. You can add constructors to the Given, When, Then and And classes to inject dependencies you might want to use.
Here's an example of a Given class that needs to use a PrismaService to set up data in a database:
class Given {
constructor(private prisma: PrismaService) {}
@Step
aProductIsInTheDatabaseWithName(name: string) {
return this.prisma.product.create({
data: { name: name },
});
}
}
and here's a When class that triggers a call to a test version of a Nest.js app:
class When {
constructor(private app: INestApplication) {}
@Step
aRequestIsMadeTo(endpoint: string) {
return request(this.app.getHttpServer())
.get(endpoint)
.then((resp) => {
responseStatus = resp.status;
responseBody = resp.body;
});
}
}
Note that we're assigning the response status and body to global variables. This is to capture the values so that we can assert on them in the Then class, like:
class Then {
@Step
theResponseStatusShouldBe(status: number) {
expect(responseStatus).toBe(status);
}
}
This is what the setup might look like for this:
let given: Given;
let when: When;
let then: Then;
let and: And;
let responseStatus: number;
let responseBody: any;
describe('Nest app:', () => {
let app: INestApplication;
let prisma: PrismaService;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
prisma = app.get<PrismaService>(PrismaService);
const { httpAdapter } = app.get(HttpAdapterHost);
app.useGlobalFilters(new PrismaClientExceptionFilter(httpAdapter));
given = new Given(prisma);
when = new When(app);
then = new Then();
and = new And();
suiteStart();
await app.init();
});
beforeEach(async () => {
await prisma.product.deleteMany();
});
afterEach(() => {
specFinished(expect.getState().currentTestName);
});
afterAll(async () => {
suiteFinished(expect.getState().currentTestName, __filename);
await prisma.$disconnect();
await app.close();
});
Often it's useful to display additional data in the test reports. For instance, if you're priming your database with some data as part of a Given step, you might want to call out something about the data, such as an entity name, or number of rows inserted. In this case, use Bddly's interestingGiven function, which takes a name for the interesting fact, and the fact itself.
You can do the same for responses, using the report function.
Interesting givens and reports will be displayed in the Bddly test output, and if the value of any interesting givens are in any of the reports, then they will be highlighted.
Here's an example using our number doubler test from before:
let store: number;
class Given {
@Step
aNumber(x: number) {
interestingGiven('The stored number', x);
store = x;
}
}
class When {
@Step
theNumberDoublerIsCalled() {
store = store * 2;
}
}
class Then {
@Step
theStoreShouldBe(x: number) {
report('The number after doubling', store);
expect(store).toBe(x);
}
}
FAQs
A nice Behaviour Driven Development testing framework for Jest
The npm package bddly receives a total of 190 weekly downloads. As such, bddly popularity was classified as not popular.
We found that bddly demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Critics call the Node.js EOL CVE a misuse of the system, sparking debate over CVE standards and the growing noise in vulnerability databases.
Security News
cURL and Go security teams are publicly rejecting CVSS as flawed for assessing vulnerabilities and are calling for more accurate, context-aware approaches.
Security News
Bun 1.2 enhances its JavaScript runtime with 90% Node.js compatibility, built-in S3 and Postgres support, HTML Imports, and faster, cloud-first performance.