visit
You want these tests to run from any machine (local machines from other developers or staging/testing environments) so you need to make sure that the data we want to test is actually in the database. Since we don’t know what is the state of the database, usually, the best practice is to set up database conditions before the test is run (using the before
function) and clear the database state after each test (using the after
function).
let userId, productIds;
before(async () => {
const products = await Product.create([
{ name: 'Product 1' },
{ name: 'Product 2' }
]);
productIds = products.map(p => p._id);
const user = await User.create({
name: 'Bruce Wayne',
email: '[email protected]',
viewedProducts: productIds
});
userId = user._id;
});
In this example, we first create two new products using the Product.create
method. Next, we create a new user with the name “Bruce Wayne“, and email “[email protected]” and we associate the created products to this user by adding product ids in the viewedProducts parameter.
test('GET /purchased-products', async () => {
const response = await request(app)
.get(`/viewed-products/${userId}`);
expect(response.statusCode).toBe(200);
expect(response.body).toEqual([
{ name: 'Product 1' },
{ name: 'Product 2' }
]);
});
In this example, the test makes a GET request to the /purchased-products/:userId
endpoint with the userId
that we created in the setup method. And it uses Jest’s expect
function to check that the response has a status code of 200 and that the response body contains the products we created in the setup method.
const request = require("supertest");
const app = require("../app");
const User = require("../models/user");
const mongoose = require("mongoose");
describe("POST /purchase", () => {
let user;
before(async () => {
await mongoose.connection.dropDatabase();
const viewedProductIds = ["productId1", "productId2"];
user = new User({
name: "testuser",
viewedProducts: viewedProductIds,
});
await user.save();
});
test("should purchase a product and update the user", async () => {
const productId = "productId2";
const response = await request(app)
.post("/purchase")
.send({ productId });
expect(response.statusCode).toBe(200);
expect(response.body).toEqual({ message: "Product purchased successfully" });
const updatedUser = await User.findOne({ name: "testuser" });
expect(updatedUser.purchasedProducts).toContain(productId);
});
});
Now imagine if you could create hundreds of tests like this, with setting up the database, making a request, and checking all of the values from the database and the response in only 1 hour!
When you run the test, Pythagora first connects to a temporary pythagoraDb
database and restores all saved documents. This way, the database state is the same during the test as it was during the capture so the test can run on any environment while NOT changing your local database. Then, Pythagora makes an API request tracking all db queries and checks if the API response and db documents are the same as they were during the capture.
For example, if the request updates the database – then, after the API returns the response, Pythagora checks the database to see if it was updated correctly.
npm install pythagora
and add one line of code right after you initialize Express. For example, if you initialize it with const app = express();
– then you add the following line of code.
if (global.Pythagora) global.Pythagora.setApp(app);
Now, you just need to run the pythagora
command and pass the .js
file you’re usually running when starting your server. For example, if you run your server with node ./path/to/your/server.js
, you would run Pythagora like this:
npx pythagora --initScript ./path/to/your/server.js
You can do this in any way you’re usually sending requests while developing or testing. You can use your browser and click around your frontend, send requests through Postman or cURL or any other way you’re used to.
You can see all captured tests with metadata in the pythagora_data
folder in the root of your project. By having this folder present, anyone from your team can run these tests regardless of the database they are connected to.
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.
So, after you captured all requests you want, you just need to add the mode parameter --mode test
to the Pythagora command to run the captured requests.
npx pythagora --initScript ./path/to/your/server.js --mode test
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. For example:
npx pythagora --initScript ./path/to/your/server.js --mode test --full-code-coverage-report
You can find the code coverage report inside pythagora_data
folder in the root of your repository. You can open the HTML view of the report by opening pythagora_data/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.
You don’t have to worry about what database your server is connected to, or the state of that database. 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. This means that tests can be run on any machine or environment, which makes testing much more efficient.
How do you write integration tests for your API server? Would you consider using Pythagora instead/along with your system?
If not, I’d love to hear what are your concerns and why this wouldn’t work for you?
To get an update about the beta release or to give a suggestion on tech (framework / database) you want Pythagora to support you can 👉 👈