如何在没有框架的情况下测试 JavaScript

问题描述 投票:0回答:5

如何在不使用额外框架(如 Mocha)的情况下测试 JavaScript 代码?是否可以创建单元测试用例、手动编写测试函数、测试代码等?

我尝试编写一个测试用例,但即使它们位于同一文件夹中,我也无法链接它们。

假设这是 main.js 文件中的一个函数

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

这是 testMain.js 文件中的测试用例

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

testCalculate();

当我尝试在 IntelliJ IDEA IDE 中运行 testMain.js 时,我收到类似于以下内容的错误

“参考错误:计算未定义”

javascript unit-testing intellij-idea
5个回答
9
投票

这取决于您是要测试 Node.js 代码还是前端代码。在这两种情况下,您都必须将被测函数“暴露”给您的测试框架。

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);
});

当您运行

node test.js
时,您应该能够看到测试结果。

前端

// 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>

在浏览器中打开

test.html
,然后打开浏览器控制台。您应该能够看到成功消息。

这样您就可以为 Node.js 和前端 JavaScript 代码编写测试用例,而无需使用 Mocha 或任何其他框架。


1
投票

为了使您的代码正常工作,您的 testMain.js 文件需要以某种方式导入您的 main.js 代码。

在main.js文件中:

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

module.exports.calculate = calculate

在 testMain.js 文件中,导入 main.js:

var main = require('main.js')

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

注意:我知道这不一定表现出良好的编码风格,只是为了证明原始问题是什么,对原始片段进行最小的更改

也就是说,通常不值得重新发明轮子并构建自己的测试框架。您能澄清一下您想避免使用现有框架的原因吗?如果您正在寻找简单性,也许像 jstinytest 这样的东西就可以了。


1
投票

我也在寻找一些解决方案,使我能够在没有外部库的情况下编写简单的测试。这在进行采访或接受采访时特别有用。我需要测试函数是异步的,并且能够使用

done
方法进行回调或基于承诺的测试

下面是我通常复制粘贴到我想要快速测试的文件中的示例。

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 };
}

这是使用示例

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,
  }
);

有一个要点,您可以在需要时分叉和使用


-2
投票

如果是 Node.js 应用程序,您只需需要另一个文件并导入另一个函数即可。如果项目使用Babel,您可以使用ES6导入从其他文件导入函数。


-3
投票

我也被同样的问题困扰了一段时间。问题是如何在没有测试框架的情况下测试你的 JavaScript 代码,因为测试框架的工作带来了很多麻烦,而且大多数时候它们都会妨碍。


  • 答案是使用断言库而不使用测试框架。例如,您可以只使用 chai 断言库,而不使用 mocha 框架

您可以简单地安装 chai

npm install chai

之后你就可以使用它了:

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

const log = console.log;

//log(should);

//const letters = "abcdef";

const letters = 555;


letters.should.be.a('string');
© www.soinside.com 2019 - 2024. All rights reserved.