I am in the process of implementing an API for the OPTIONS request for a pre-flight check on CORS calls.

The Allowed-Origins host between local, test and prod will all be different so I moved this to a dotenv file.

Now though when I create my unit test to validate this there is an issue

Lets say that local has a value of localhost:3000 but test has test.site.com and prod is site.com

So now the test that verify headers


Will only pass in local.

A couple ideas I had and reason I think they would not cover the cases:

Hard code like above each env condition would leave failing test in other environment.

Load the env files and validate that but this is useless because I am confirming the file equals the file.

Load the env file for each env and hardcode the expectation in the assert. Could work the plan is not to have local or prod in test.

  • 1
    Tests should be written with a given test rig in mind. It is possible to describe a test rig that makes a production deployment and a local test rig look identical, but I would avoid that. It is far too easy for a developer to write a destructive/mutative test expecting it to only be run against a throwaway build. This is why we make the distinction between dev tests, and deployment tests - even if they superficially look identical. Written as a dev test, hard code the url, it simplifies development. Written as a deployment test, use a configuration object obtained somehow. – Kain0_0 Jan 15 at 4:34
  • @Kain0_0 never looked into deployment tests. Seems like this may be a path I go down for this goal. – nerdlyist Jan 15 at 13:20

What do you want to verify in your test?

  • If you want to verify that the Access-Control-Allow-Origin header is correctly filled according to the environment you are running in, then you should exactly read the expected value from the dotenv file, so you can know that its value is filled in correctly in the header.

    You would need a proper review process to ensure that the value stored in the dotenv file is actually correct.

  • If you want to verify that the Access-Control-Allow-Origin header matches one of "localhost:3000","test.site.com" or "site.com", then you should write a test for that, but such a test would not verify that the correct value for the current environment gets used. The test would also need to be updated if the URL of an environment changes or a new URL gets added.

  • From my perspective the first case is testing that the file matches the file which seems fruitless in my opinion because of course it will. Also, a review process would include me, myself and I and I trust none of those guys. I want the test to break like the second case states because then I know I changed something and need to go check but then the trap of a set of test per an env or all envs tested in one place? – nerdlyist Jan 15 at 13:18

"The environment" usually has the same interface as a Map/Dictionary, or a lookup function -- you pass in a string, you get a string returned.

// getenv: String -> String
String path = System.getenv("PATH")

My first choice is generally to run the tests in the correctly specified environment. After all, that's what the environment is for.

But if I need to test different environments, then what I'll normally do is partition my code into two parts; one part that knows that environment variables are fetched using System.getenv, and a second part that knows which environment variables we get and what we do with them.

doSomethingInteresting(x: X, y: Y, z: Z) {
    // because this method is so simple, the risk of a mistake is low
    // especially in a language where we have strong type checking
    doSomethingInteresting(x, y, z, System::getenv)

doSomethingInteresting(x: X, y: Y, z: Z, getenv: String -> String) {
    // This method is potentially very complicated, and cares about
    // which values are returned by getenv, but it doesn' particularly
    // care whether we pass in the real getenv, or a substitute

    // ...

My "unit tests" that need to customize the environment invoke the second version of the function, where all of the risk is concentrated.

Passing getenv around is a lot of hammer; if we have a simple nail, then maybe we can do the same thing on a smaller scale. For example

doSomethingInterestingWithThePath(x: X, y: Y, z: Z) {
    // we don't need to test this
    doSomethingInterestingWithThePath(x, y, z, System.getenv("PATH"))

doSomethingInterestingWithThePath(x: X, y: Y, z: Z, path: String) {
    // instead we test this
    // ...

As an implementor, you are expected to make design choices that ensure that testing your implementation is cost effective....

  • Thank you for the answer. It triggered a thought. I have resources for each env now to specify what to expect in those environments. It lets me check per env which is what I wanted. Prod is still tricky. – nerdlyist Jan 20 at 15:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.