最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

unit testing - How to test JavaScript without a framework - Stack Overflow

programmeradmin3浏览0评论

How can I test JavaScript code without using an additional framework such as Mocha? Is it possible to create a unit test case, write test functions manually, test the code, etc.?

I've tried to write a test case, but even though they were in the same folder, I couldn't link them.

Let's say this is a function in the main.js file

function calculate(a, b) {
    return a + b;
}

And this is a test case in the testMain.js file

function testCalculate(){
    if(calculate(1, 1) == 2)
       console.log('It Works!');
    else
       console.log('Test failed');
}

testCalculate();

When I try to run testMain.js in the IntelliJ IDEA IDE I get an error similar to

"ReferenceError: calculate is not defined"

How can I test JavaScript code without using an additional framework such as Mocha? Is it possible to create a unit test case, write test functions manually, test the code, etc.?

I've tried to write a test case, but even though they were in the same folder, I couldn't link them.

Let's say this is a function in the main.js file

function calculate(a, b) {
    return a + b;
}

And this is a test case in the testMain.js file

function testCalculate(){
    if(calculate(1, 1) == 2)
       console.log('It Works!');
    else
       console.log('Test failed');
}

testCalculate();

When I try to run testMain.js in the IntelliJ IDEA IDE I get an error similar to

"ReferenceError: calculate is not defined"

Share Improve this question edited Jan 11, 2021 at 7:52 Peter Mortensen 31.6k22 gold badges110 silver badges133 bronze badges asked Oct 4, 2019 at 19:25 ÇağlarÇağlar 1012 silver badges8 bronze badges 1
  • 2 Just like you would in Java: you still need to have an execution context that includes the functionality under test. And just like in Java I see essentially zero benefit to not using a test framework. – Dave Newton Commented Oct 4, 2019 at 19:31
Add a ment  | 

5 Answers 5

Reset to default 10

It depends whether you are trying to test Node.js code or front end code. In both cases you have to "expose" the function under test to your test framework.

Node.js

// main.js

const obj = {};

obj.sum = (a, b) => {
  return a+b;
};

module.exports = obj; // Export 'obj' so that it is visible from your test runner

// test.js
const main = require('main.js');
const assert = require('assert');

const it = (desc, fn) => {
  try {
    fn();
    console.log('\x1b[32m%s\x1b[0m', `\u2714 ${desc}`);
  } catch (error) {
    console.log('\n');
    console.log('\x1b[31m%s\x1b[0m', `\u2718 ${desc}`);
    console.error(error);
  }
};

it('should return the sum of two numbers', () => {
  assert.strictEqual(main.sum(5, 10), 15);
});

When you run node test.js you should be able to see the test result.

Front End

// app.js
self.myapp = myapp; // All the methods in myapp will be exposed globally

myapp.sum = function(a, b) {
  return a + b;
}

// test.js
function it(desc, fn) {
  try {
    fn();
    console.log('\x1b[32m%s\x1b[0m', '\u2714 ' + desc);
  } catch (error) {
    console.log('\n');
    console.log('\x1b[31m%s\x1b[0m', '\u2718 ' + desc);
    console.error(error);
  }
}

function assert(condition) {
  if (!condition) {
    throw new Error();
  }
}

it('should return a sum of two integers', function(){
  assert(myapp.sum(5, 10) === 15);
});


// test.html - This is your test runner for the front end
<html>
...
<body>
...
<script src="app.js"></script>
<script src="test.js"></script>
</body>
</html>

Open test.html in a browser and open the browser console. You should be able to see the success message.

This way you can write test cases for Node.js and front end JavaScript code without using Mocha or any other framework.

To make your code work, your testMain.js file needs to import your main.js code somehow.

In the main.js file:

function calculate(a, b) {
    return a+b;
}

module.exports.calculate = calculate

in testMain.js file, import the main.js:

var main = require('main.js')

function testCalculate(){
    if(main.calculate(1+1)==2)
       console.log('It Works!');
    else
       console.log('Test failed');
}

Note: I'm aware this isn't necessarily showing good coding style, just aiming to demonstrate what the original issue was with minimal changes to the original snippets

That said, it's not usually worth reinventing the wheel and building your own test framework. Can you clarify the reason why you would like to avoid an existing framework? If you are looking for simplicity, maybe something like jstinytest would do the trick.

I also was looking for some solutions that would allow me to write simple tests without external libraries. This is especially useful when conducting interviews or being interviewed. I needed the test functions to be asynchronous and to be able to use done method for callback or promise-based tests

Below is the example that I usually copy-paste to the file I want to quickly test.

type Config = {
  logPerformance?: boolean;
  timeout?: number; // Timeout in milliseconds
};

function createTestRunner(initialConfig?: Config) {
  let globalConfig: Config = {
    logPerformance: false,
    timeout: 5000,
    ...initialConfig,
  };

  const assert = {
    condition: (condition: boolean, message?: string) => {
      if (!condition) {
        throw new Error(message || "Assertion failed: condition is false");
      }
    },
    isDeepEqual: (a: any, b: any, message?: string) => {
      const stringify1 = JSON.stringify(a);
      const stringify2 = JSON.stringify(b);

      if (stringify1 !== stringify2) {
        throw new Error(
          message ||
            `Assertion failed: values are not equal ${stringify1} !== ${stringify2}`
        );
      }
    },
    shouldThrow: (fn: Function) => {
      const message = "Assertion failed: the function hasn't thrown";
      try {
        fn();
        throw new Error(message);
      } catch (e) {
        if (e instanceof Error && e.message === message) {
          throw e;
        }
        return true;
      }
    },
  };

  function setConfig(config: Config) {
    globalConfig = { ...globalConfig, ...config };
  }

  function it(
    desc: string,
    fn: (done: (error?: any) => void) => void | Promise<void>,
    config?: Config
  ) {
    const { logPerformance, timeout } = { ...globalConfig, ...config };
    const startTime = Date.now();

    const testPromise = executeTestFunction(fn, timeout);

    handleTestResult(testPromise, desc, startTime, logPerformance);
  }

  function executeTestFunction(fn: Function, timeout?: number): Promise<void> {
    return new Promise<void>((resolve, reject) => {
      let doneCalled = false;

      const done = (error?: any) => {
        if (doneCalled) {
          reject(new Error("done() called multiple times"));
          return;
        }
        doneCalled = true;
        if (error) {
          reject(error);
        } else {
          resolve();
        }
      };

      try {
        const result = fn.length > 0 ? fn(done) : fn();

        if (result instanceof Promise) {
          result.then(resolve).catch(reject);
        } else if (fn.length === 0 && result === undefined) {
          // Synchronous test passed
          resolve();
        }

        if (fn.length > 0 && result === undefined) {
          const timeoutDuration = timeout ?? globalConfig.timeout ?? 5000;
          setTimeout(() => {
            if (!doneCalled) {
              reject(new Error("Test timed out: done() was not called"));
            }
          }, timeoutDuration);
        }
      } catch (error) {
        reject(error);
      }
    });
  }

  function handleTestResult(
    testPromise: Promise<void>,
    desc: string,
    startTime: number,
    logPerformance?: boolean
  ) {
    testPromise
      .then(() => {
        logTestSuccess(desc, startTime, logPerformance);
      })
      .catch((error) => {
        logTestFailure(desc, startTime, error, logPerformance);
      });
  }

  function logTestSuccess(
    desc: string,
    startTime: number,
    logPerformance?: boolean
  ) {
    const endTime = Date.now();
    let message = `\x1b[32m\u2714 ${desc}\x1b[0m`;
    if (logPerformance) {
      const duration = endTime - startTime;
      message += ` (Duration: ${duration} ms)`;
    }
    console.log(message);
  }

  function logTestFailure(
    desc: string,
    startTime: number,
    error: any,
    logPerformance?: boolean
  ) {
    const endTime = Date.now();
    let message = `\n\x1b[31m\u2718 ${desc}\x1b[0m`;
    if (logPerformance) {
      const duration = endTime - startTime;
      message += ` (Duration: ${duration} ms)`;
    }
    console.log(message);
    console.error(error);
  }

  // Return the methods
  return { it, assert, setConfig };
}

This is the Usage example

const { it, assert, setConfig } = createTestRunner({
  logPerformance: true,
  timeout: 5000,
});

// Synchronous test
it("should add numbers correctly", () => {
  const result = 1 + 1;
  assert.condition(result === 2, "1 + 1 should equal 2");
});

// Promise-based asynchronous test
it("should resolve after 1 second", () => {
  return new Promise<void>((resolve) => {
    setTimeout(() => {
      assert.condition(true);
      resolve();
    }, 1000);
  });
});

// Callback-based asynchronous test with custom timeout
it(
  "should call done after async operation",
  (done) => {
    setTimeout(() => {
      assert.condition(true);
      done();
    }, 3000);
  },
  {
    timeout: 4000,
  }
);

there is a gist that you can fork and use when needed

If it is a Node.js application you can simply require the other file and import the other function. If the project uses Babel you can use ES6 import to import the function from the other file.

I was also bothered by the same issue for a while. The question is how to test your JavaScript code with out a testing framework since testing frame works bring a lot to the table and most of the time they get into the way.


  • The answer is to use assertion libraries without the testing frame work. For example you can just use chai assertion library with out mocha frame work

you can simple install chai with npm install chai
After that you can just use it:

var should = require('chai').should() 

const log = console.log;

//log(should);

//const letters = "abcdef";

const letters = 555;


letters.should.be.a('string');
发布评论

评论列表(0)

  1. 暂无评论