mcshakes
mcshakes

Reputation: 145

Better testing Express routes with test DB in postgreSQL

I'm trying to get better at writing tests for my Node.js APIs. My latest project has me querying an endpoint /api/v1/restaurants that returns data including an array of objects. This is the functioning call within the controller:

restaurantRouter.get("/api/v1/restaurants", async (req, res) => {
    try {
        // const results = await db.query("SELECT * FROM restaurants");
        const results = await select("restaurants");

        res.status(200).json({
            status: "success",
            results: results.rows.length,
            data: {
                restaurants: results.rows
            }
        })
    } catch (err) {
        console.log(err)
    }
})

My problem comes when I try to make API calls in my tests like so:

        test("restaurants are returned as JSON", async () => {
            await api
                .get("/api/v1/restaurants")
                .expect(200)
                .expect('Content-Type', /application\/json/)
        })

        test("all restaurants are returned", async () => {
            const response = await api.get("/api/v1/restaurants")

            expect(response.body.data).not.toBeEmpty()
            expect(response.body.data.restaurants).toBeArrayOfSize(2)
        })

They immediately default to the Dev database table restaurants. My immediate thought was to just do the easy/lazy thing and create an identical route for tests like this:

restaurantRouter.get("/api/v1/test-restaurants", async (req, res) => {
    try {
        const results = await select("test_restaurants");

        res.status(200).json({
            status: "success",
            results: results.rows.length,
            data: {
                restaurants: results.rows
            }
        })
    } catch (err) {
        console.log(err)
    }

})

Is there a better, or more elegant way to go about testing these? Maybe using a middleware to pass in an optional variable that defaults to Dev/Prod database if no variable parameter is given?

I've been searching over the course of a couple of days and all the tutorials and examples have people using mongoDB, which seems to be easier to set up and tear down testing databases. Thanks, and let me know if more info is needed.

Upvotes: 3

Views: 1625

Answers (2)

mcshakes
mcshakes

Reputation: 145

To add some clarity to how I used @Odunsi's answer:

My db/index.js file:

const { Pool } = require("pg")

const PG_DATABASE = process.env.NODE_ENV === "test" 
? process.env.TEST_DATABASE 
: process.env.DEV_DATABASE


const pool = new Pool({
    user: process.env.PGUSER,
    host: process.env.PGHOST,
    database: PG_DATABASE,
    port: 5432,
    password: null
})

pool.on('connect', () => {
    console.log(`Connected to the DB: ${process.env.NODE_ENV}`);
});

module.exports = {
    query: (text, params) => pool.query(text, params)
}

And the key was passing in the NODE_ENV with the scripts (as the answer above shows), but I didn't have to use cross-env module.

 "scripts": {
    "test": "NODE_ENV=test jest --verbose --runInBand",
    "dev": "NODE_ENV=development nodemon index.js"
  },

And now I'm using two different databases.

Upvotes: 1

Odunsi
Odunsi

Reputation: 490

Since you have two databases, restaurants and test_restaurants.

Edit your test command in package.json

"test": "cross-env NODE_ENV=test jest".

Then make your app use the test_restaurants database if NODE_ENV is test in your config.

if(process.env.NODE_ENV === "test"){
    // use test database
}

if(process.env.NODE_ENV !== "test"){
    // use production database
}


Upvotes: 1

Related Questions