# Mocha Testing in Separate Files and Dotenv

# Introduction
The main goal of dividing tests into multiple files is to make tests readable and easier to maintain. Each file should be responsible for a single responsibility. Here is how I separate tests into multiple files:

# Step by step
First, we need to specify which files mocha will test. I personally create`*.test.js` to specify that mocha will only run files with `.test.js`. So another file that should be a part of the main test will be ignored. For this example, I will create a math module. 

Let's say our project has `math_problem.js` file in `src` as our module.
```
const sumData = (a, b, c = 0) => {
  return a + b + c;
};

module.exports = { sumData };
```
I assume our project already has `mocha` and `chai` in project dependencies. Complete source code available in my GitHub [repository](https://github.com/hermansyah32/separate-mocha-test)

## Test in separate test files
For the first problem, create part of test `1. problem_one.part.js` in `tests/modules` directory

```
const { sumData } = require("../../src/math_problem");
const { expect } = require("chai");

const problem_one_test = () => {
  it("has to be 4", () => {
    const result = sumData(2, 2);
    expect(result).eql(4);
  });
};

module.exports = problem_one_test;

``` 
Then for the test file create `function.test.js` in `tests` directory.
```
process.env.NODE_ENV = "testing";

const problemOne = require("./modules/1. problem_one.part");

describe("math problem", () => {
  describe("problem one", problemOne.bind(this));
});
```
In this file, I force Node environment to `testing`, it is not required for this project, but this would help if you need a specific environment for testing.

Finally in your `package.json` add test command in `scripts` section
```
"scripts": {
    "test": "mocha tests/*.test.js --exit"
  },
```
Done!.
Now you can run mocha test in separate files by run `npm test` or `npm run test`.

## Pass parameter to separate test files 
Let's say you already create a variable that would be required for test part files.  To pass a variable from main test file to part file, we create a parent function.
Now, create `2. problem_two.part.js` in `tests/moduels` directory.
```
const { sumData } = require("../../src/math_problem");
const { expect } = require("chai");

const problem_two_test = (c) => {
  return function () {
    it("has to be 8", () => {
      const result = sumData(2, 2, c);
      expect(result).eql(8);
    });
  };
};

module.exports = problem_two_test;
```
Assume in our test, `c` variable will be added from `c` param in `problem_two_test` function. So `c`  variable will be required for tests available in `2. problem_two.part.js` file.

In `function.test.js` file, import problem two by adding code below after `problemOne` variable
```
const problemTwo = require("./modules/2. problem_two.part")(4);
```
and add another decribe test for `problemTwo`
```
describe("problem two", problemTwo.bind(this));
```
So`function.test.js` file will be like this
```
process.env.NODE_ENV = "testing";

const problemOne = require("./modules/1. problem_one.part");
const problemTwo = require("./modules/2. problem_two.part")(4);

describe("math problem", () => {
  describe("problem one", problemOne.bind(this));
  describe("problem two", problemTwo.bind(this));
});

```

Done, Now we can pass variable from main function to test part file.

> in this line `const problemTwo = require("./modules/2. problem_two.part")(4);` we pass `4` as `c` param in `2. problem_two.part.js` file. co variable `problemTwo` will return test functions and already has `c` param.

## Ability for single file test
Breaking tests into multiple test files will benefit easier and more readable code. But how if you want to test only a single file before being added to the main test case? We don't want to test every test just to make sure that a specific test is right.

In order to test a single test file running, we create describe test in every part files but if only it run in single test command. So to make a difference from batch test, in `function.test.js` we set a `TEST_MODE` environment variable. Add `process.env.TEST_MODE = "batch";` below NODE_ENV.
So `function.test.js` file will be like this:
```
process.env.NODE_ENV = "testing";
process.env.TEST_MODE = "batch";

const problemOne = require("./modules/1. problem_one.part");
const problemTwo = require("./modules/2. problem_two.part")(4);

describe("math problem", () => {
  describe("problem one", problemOne.bind(this));
  describe("problem two", problemTwo.bind(this));
});
```

And for each part files, add describe test, so each file will be like this below.
`1. problem_one.part.js` file: 
```
const { sumData } = require("../../src/math_problem");
const { expect } = require("chai");

const problem_one_test = () => {
  it("has to be 4", () => {
    const result = sumData(2, 2);
    expect(result).eql(4);
  });
};

if (process.env.TEST_MODE !== "batch") {
  describe("problem one", problem_one_test.bind(this));
}

module.exports = problem_one_test;
```
`2. problem_two.part.js` file:
```
const { sumData } = require("../../src/math_problem");
const { expect } = require("chai");

const problem_two_test = (c) => {
  return function () {
    it("has to be 8", () => {
      const result = sumData(2, 2, c);
      expect(result).eql(8);
    });
  };
};

if (process.env.TEST_MODE !== "batch") {
  describe("problem one", problem_two_test(4).bind(this));
}

module.exports = problem_two_test;
```
In `package.json` file, add `test-single` command, so `scripts` section will be like this
```
  "scripts": {
    "test": "mocha tests/*.test.js --exit",
    "test-single": "mocha --exit"
  },
```
now you can test each file by run command like this
`npm run test-single-env '.\tests\modules\1. problem_one.part.js'`
## Load .env files
What if you have a `.env` file for testing? You want to create a specific environment for your test. There are many ways to load Node environment from `.env` file. You will need `dotenv` dependency here. Assume you have already installed it in your project. For example, we create third test case.
Create `3. problem_three.part.js` in `tests/modules` directory. Write code like below
```
const { sumData } = require("../../src/math_problem");
const { expect } = require("chai");

const problem_three_test = () => {
  it("has to be 10", () => {
    const result = sumData(2, 2, Number(process.env.STATIC_NUMBER));
    expect(result).eql(10);
  });
};

if (process.env.TEST_MODE !== "batch") {
  describe("problem three", problem_three_test.bind(this));
}

module.exports = problem_three_test;
```
Now add test case in `functions.test.js` file
```
process.env.NODE_ENV = "testing";
process.env.TEST_MODE = "batch";

const problemOne = require("./modules/1. problem_one.part");
const problemTwo = require("./modules/2. problem_two.part")(4);
const problemThree = require("./modules/3. problem_three.part");

describe("math problem", () => {
  describe("problem one", problemOne.bind(this));
  describe("problem two", problemTwo.bind(this));
  describe("problem three", problemThree.bind(this));
});
```

Dotenv will be loaded in script command, so make test command that require dotenv/config in `package.json` file. I will define specific file `.env.` for testing environment. 
In `.env.testing` file, assume we have STATIC_NUMBER environment variable
```
STATIC_NUMBER=6
```
For `package.json` file add script command below
```
  "scripts": {
    "test": "mocha tests/*.test.js --exit",
    "test-single": "mocha --exit",
    "test-env": "mocha -r dotenv/config tests/*.test.js --exit dotenv_config_path=./.env.testing",
    "test-single-env": "mocha -r dotenv/config --exit dotenv_config_path=./.env.testing"
  },
```
Now the `test-env` and `test-single-env` commands will automatically load `.env.testing` file to environment variable.

> You might notice a warning message `Cannot find any files matching pattern "dotenv_config_path=./.env.testing"`. This is because mocha also read this as spec test file param.


# Reference
1. Project repository: [Github](https://github.com/hermansyah32/separate-mocha-test)
2. Related Article: [How to separate mocha test](https://alxolr.com/articles/how-to-separate-mocha-tests-in-multiple-files)
