Developers spend 20-30% of their time writing tests!
With Pythagora, devs spend 0.2% time on tests while having 100% code coverage w/ negative tests
๐๏ธ โถ๏ธ Video resources โถ๏ธ ๐๏ธ
Pythagora Demo (4 min)
Pythagora Tech Deep Dive (16 min)
Dev Workflow With Pythagora (4 min)
๐๏ธ How it works
To integrate Pythagora into your Node.js app, you just need to install the pythagora package
npm install pythagora
and run the Pythagora capture command. Then, just play around with your app and from all API requests and database queries Pythagora will generate integration tests.
Pythagora records all requests to endpoints of your app with the response and everything that's happening during the request. Currently, that means all Mongo and Redis queries with responses (in the future 3rd party API requests, disk IO operations, etc.). Then, when you run the tests, Pythagora can simulate the server conditions from the time when the request was captured. 1. Capturing requests
When running tests, it doesnโt matter what database is your Node.js connected to or what is the state of that database. Actually, that database is never touched or used โ> instead, Pythagora creates a special, ephemeral `pythagoraDb` database, which it uses to restore the data before each test is executed, which was present at the time when the test was recorded. Because of this, tests can be run on any machine or environment. 2. Running tests
If a test does an update to the database, Pythagora also checks the database to see if it was updated correctly.
โ๏ธ Setup
- Install Pythagora by running
And that's it! You are ready to start recording your integration tests!npm install pythagora
๐ฅ Capturing tests
- From the root directory run Pythagora in a capture mode first to capture test data and mocks.
Eg. if you start your Node.js app withnpx pythagora --init-command "my start command" --mode capture
nest start
then the command would be:
npx pythagora --init-command "nest start" --mode capture
- Click around your application or make requests to your API. Pythagora will capture all requests and responses.
NOTE: To stop the capture, you can exit the process like you usually do (Eg.Ctrl + C
)
โถ๏ธ Executing tests
After you captured all requests you want, you just need to change the mode parameter to --mode test
in the Pythagora command.
npx pythagora --init-command "my start command" --mode test
๐ Options
These are available options for Pythagora command:
--rerun-all-failed (runs again only tests that failed in previous run)
npx pythagora --init-command "my start command" --mode test --rerun-all-failed
--delete-all-failed (deletes all previously failed tests)
npx pythagora --init-command "my start command" --delete-all-failed
--delete testId (deletes test with testId)
npx pythagora --init-command "my start command" --delete testId
--pick endpoint1 endpoint2 (starts capturing only listed endpoints)
npx pythagora --init-command "my start command" --mode capture --pick /endpoint1 /endpoint2
--ignore endpoint1 endpoint2 (starts capturing but ignores all listed endpoints)
npx pythagora --init-command "my start command" --mode capture --ignore /endpoint1 /endpoint2
--review (runs review process of failed tests, allowing you to update old captured tests or to delete them)
npx pythagora --review
๐ Code Coverage Report
Code coverage is a great metric while building automated tests as it shows us which lines of code are covered by the tests. Pythagora uses nyc
to generate a report about code that was covered with Pythagora tests. By default, Pythagora will show you the basic code coverage report summary when you run tests.
If you want to generate a more detailed report, you can do so by running Pythagora with --full-code-coverage-report
flag. Eg.
npx pythagora --init-command "my start command" --mode test --full-code-coverage-report
You can find the code coverage report inside pythagora_tests
folder in the root of your repository. You can open the HTML view of the report by opening pythagora_tests/code_coverage_report/lcov-report/index.html
.
In case you don't want the code coverage to be shown at all while running tests, you can run the tests with --no-code-coverage
parameter. This is helpful
during debugging process since the code coverage report can clash with your IDE's debugger.
๐ Authentication
For authentication we support JWT, sessions stored in
Redis and sessions stored in MongoDB.
First 2 cases cases (JWT and sessions stored in Redis) should work just fine without
any additional implementation but for session that are stored in MongoDB you need to add this one line of code:
if (global.Pythagora) global.Pythagora.authenticationMiddleware = true;
just before your authentication middleware. For example, if you are using express-session you would have to add our line of code just above your middleware that is managing sessions in your DB, like this:
if (global.Pythagora) global.Pythagora.authenticationMiddleware = true;
app.use(session({
secret: 'my-secret',
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 60 * 60 * 1000
},
store: MongoStore.create({
mongoUrl: mongourl,
mongoOptions: {
useNewUrlParser: true,
useUnifiedTopology: true
}
})
}));
That's it! You are ready to go and all your API requests with authentication should PASS!
๐บ๏ธ ๏ธ Where can I see the tests?
Each captured test is saved in pythagora_tests directory at the root of your repository.
Each JSON file in this repository represents one endpoint that was captured and each endpoint can have many captured tests.
If you open these files, you will see an array in which each object represents a single test. All data that's needed to run a test
is stored in this object. Here is an example of a test object.
{
"id": "b47cbee2-4a47-4b2c-80a0-feddae3081b3",
"endpoint": "/api/boards/", // endpoint that was called
"body": {}, // body payload that was sent with the request
"query": {}, // query params that were sent with the request
"params": {}, // params that were sent with the request
"method": "GET", // HTTP method that was used
"headers": { // headers that were sent with the request
"x-forwarded-host": "localhost:3000",
...
},
"statusCode": 200, // status code that was returned
"responseData": "...", // response data that was received
"intermediateData": [ // server activity that was captured during the request
{
"type": "mongodb", // type of the activity - mongo query in this case
"op": "findOneAndUpdate",
"db": "ecomm",
"collection": "users",
"query": { // mongo match query that was executed
"_id": "ObjectId(\"63f5e8272c78361761e9fcf1\")"
},
"otherArgs": {
"update": { // data that needs to be updated
"$set": {
"name": "Steve",
...
}
},
...
},
"options": {
"upsert": false,
...
},
"preQueryRes": [ // data that was present in the database before the query was executed
{
"_id": "ObjectId(\"63f5e8272c78361761e9fcf1\")",
"name": "Michael",
...
}
],
"mongoRes": [ // data that was returned by the query
{
"_id": "ObjectId(\"63f5e8272c78361761e9fcf1\")",
"name": "Steve",
...
}
],
"postQueryRes": [ // data that was present in the database after the query was executed
{
"_id": "ObjectId(\"63f5e8272c78361761e9fcf1\")",
"name": "Steve",
...
}
]
}
],
"createdAt": "2023-02-22T14:57:52.362Z" // date when the test was captured
}
โ๏ธ Support
For now, we support projects that use:
- Express (it can be used explicitly or under the hood, like in Apollo server or NestJS)
- MongoDB (same as express, it can be used explicitly or under the hood, like with Mongoose)
- Redis
Other technologies that Pythagora works with:
Apollo server | GraphQL | NestJS | Next.js | Nuxt.js | PostgreSQL |
---|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
Upcoming | Upcoming | Upcoming |