Search code examples
javascriptjestjschai-http

Sending Mapquest/Google API Key In Jest Using ChaiHttp


Context: I have a back-end API application that I'm working on where I have series of tests for the API. The project is here: https://github.com/jeffnyman/devcamper_api. This project has a series of tests that I've been developing. As far as the test tooling, I'm using ChaiHttp, Jest, and mongodb-memory-server. I should probably note that this is my first real JavaScript project and I'm learning the test tooling as I go.

Problem: All of those tests were working great ... up until I added some geocoder functionality. Specifically, I added the node-geocoder package.

Error Condition:

I get the following error on some of my routes when the tests are run:

OperationalError {
cause: Error: Status is REQUEST_DENIED. You must use an API key to
authenticate each request to Google Maps Platform APIs.

As a brief review of my logic:

I have a Bootcamp model that has the geocoder logic added to it. Specifically, that logic takes the address passed on from the request, breaks it up, geocodes it, and then discards the address that is passed in, creating a formattedAddress. You can see the specific geocoder logic is in the model. The node-geocoder itself is set up in my geocoder module.

As you can see in my geocoder file, I am including the a check for the GEOCODER_API_KEY. You can't see the file in the repo that contains this key (I have it in .gitignore) but it is there in my local project.

Crucially: when I run the API call through Postman, everything runs correctly. So I know all of the logic is hooked up and working correctly.

So it looks like the issue is solely when running the tests, the API key is not being found and used as part of the request. Specifically, this line in my geocoder is where the error is being generated:

const loc = await geocoder.geocode(this.address);

However, I'm not mocking anything here except for the fact that I'm using an in-memory mongo database. So it's unclear to me why the logic from the model is not being fired off correctly when the tests are being run.

From other questions, I have tried adding a .set line to my test, like this:

chai
  .request(server)
  .post("/api/v1/bootcamps")
  .send(bootcamp)
  .set("apiKey", process.env.GEOCODER_API_KEY)
  .end((err, res) => {
    ....

However, that doesn't seem to do the trick. I get the same error.

So the I tried putting in a console.log(options); in my geocoder.js file. And, lo and behold, that returns this:

{
   provider: undefined,
   httpAdapter: 'https',
   apiKey: undefined,
   formatter: null
}

Well, that seems to be a problem! The options are set up like this:

const options = {
  provider: process.env.GEOCODER_PROVIDER,
  httpAdapter: "https",
  apiKey: process.env.GEOCODER_API_KEY,
  formatter: null,
};

So notice my provider and my apiKey are coming back undefined, which implies the process.env parts are not being read correctly or at all. That explains why this isn't working but it doesn't explain why it's only not working with the tests.


Solution

  • Okay, I figured it out. I was on the right track with my process.env findings. The solution was as such:

    In package.json I had to add this:

      "jest": {
        "setupFiles": [
          "./config/jestVars.js"
        ]
    

    It doesn't matter where the file is or what it's called but what is important is that you create a file that has the environment variables you need. So my jestVars.js would look something like this:

    process.env.GEOCODER_PROVIDER = "mapquest";
    process.env.GEOCODER_API_KEY = "<consumer key>";
    

    Once that's in place, Jest has the environment variables I need. It's very unclear to me why this is necessary because it feels like Jest is then not actually executing the logic as it is set up in the project. Then again, these are only variables and not strict logic, so there is probably some underlying reason having to do with how Jest interacts with the process.

    In any event, while this looked like a ChaiHttp issue at first it's clearly a Jest issue.