ホームページ  >  記事  >  ウェブフロントエンド  >  実行可能な JavaScript 仕様を構築する方法の詳細な説明

実行可能な JavaScript 仕様を構築する方法の詳細な説明

青灯夜游
青灯夜游転載
2020-10-23 17:57:181748ブラウズ

実行可能な JavaScript 仕様を構築する方法の詳細な説明

プログラミングは、単にタスクを完了する方法をコンピュータに指示するだけではなく、他の人、さらには将来の自分にアイデアを正確に伝えることでもあります。このようなコミュニケーションには、情報を共有することや、単に改訂を容易にすることなど、複数の目的がある場合があります。これは、昔に行われた内容を理解していないか覚えていない場合は困難です。

ソフトウェアを作成するときは、コードが意図した機能を備えていることを確認する必要もあります。セマンティクスを定義する正式な方法はありますが、最も単純かつ最速の (ただし厳密さはそれほど厳しくない) アプローチは、機能を使用して、期待どおりの結果が得られるかどうかを確認することです。

ほとんどの開発者は、コード ブロックの目標を明確にするためのコメントとしてのコード ドキュメントと、関数が目的の出力を与えることを確認するための一連のテストという実践に慣れています。

しかし、通常、文書化とテストは別の手順で行われます。これらのプラクティスを統合することで、プロジェクト開発に携わるすべての人に、より良いエクスペリエンスを提供できます。この記事では、ドキュメントとテストの両方に適した JavaScript 仕様を実行できる単純なプログラムの実装について説明します。

ディレクトリ内のすべての仕様ファイルを検索し、各仕様で見つかったすべてのアサーションを抽出し、その結果を計算し、最後にどのアサーションが失敗し、どのアサーションが失敗したかを表示するコマンド ライン インターフェイスを構築します。

仕様の形式

各仕様ファイルは、テンプレート テキストから文字列をエクスポートします。最初の行は仕様のタイトルとして機能します。テンプレート リテラルを使用すると、文字列の間に JS 式を埋め込むことができ、各式はアサーションを表します。各アサーションを識別するために、行を一意の文字で始めることができます。

この例では、バー文字 (|) とダッシュ (-) の組み合わせを使用できます。ダッシュは回転ドアの記号に似ており、論理アサーションの記号として表すこともできます。 。

これは、その使用法を説明した例です:

const dependency = require('./dependency')module.exports = `
  Example of a Specification File
  
  This project allows to test JavaScript programs using specification files.
  Every *.spec.js file exports a single template literal that includes a general
  explanation of the file being specified. Each file represents a logical
  component of a bigger system. Each logical component is composed of several
  units of functionality that can be tested for certain properties.
  Each one of this units of functionality may have one or more
  assertions. Each assertion is denoted by a line as the following:

  |- ${dependency} The dependency has been loaded and the first assert has
  been evaluated.

  Multiple assertions can be made for each file:

  |- ${false} This assertion will fail.

  |- ${2 + 2 === 4} This assertion will succeed.

  The combination of | and - will form a Turnstile ligature (|-) using the appropriate
  font. Fira Code is recommended. A Turnstile symbol was used by Gottlob Frege
  at the start of sentenses being asserted as true.

  The intended usage is for specification-first software. Where the programmer
  defines the high level structure of a program in terms of a specification,
  then progressively builds the parts conforming that specification until all
  the tests are passed. A desired side-effect is having a simple way to generate
  up-to-date documentation outside the code for API consumers.
`

次に、プログラムの高レベルの構造に移りましょう。

プログラムの構造

プログラムの構造全体は、2 つの Node.js ライブラリを使用して処理することに加えて、数行のコードで定義できます。ファイル システム (fs ) とディレクトリ パス (path) には依存関係はありません。このセクションではプログラムの構造のみを定義します。関数の定義は次のセクションで説明します。

#!/usr/bin/env node
const fs = require('fs')
const path = require('path')
const specRegExp = /\.spec\.js$/
const target = path.join(process.cwd(), process.argv[2])
// Get all the specification file paths
// If a specification file is provided then just test that file
// Otherwise find all the specification files in the target directory
const paths = specRegExp.test(target)
  ? [ target ]
  : findSpecifications(target, specRegExp).filter(x => x)
// Get the content of each specification file
// Get the assertions of each specification file
const assertionGroups = getAssertions(getSpecifications(paths))
// Log all the assertions
logAssertions(assertionGroups)
 
// Check for any failed assertions and return an appropriate exit code
process.exitCode = checkAssertions(assertionGroups)

これは CLI (コマンド ライン インターフェイス) のエントリ ポイントでもあるため、このファイルがノード プログラムによって実行される必要があることを示すシバンの最初の行を追加する必要があります。単一のパラメータのみを対象としているため、コマンド オプションを処理するために特定のライブラリを追加する必要はありません。ただし、このプログラムを大幅に拡張する予定がある場合は、他のオプションを検討することもできます。

ターゲットのテスト ファイルまたはディレクトリを取得するには、実行されたコマンドへのパス (process.cwd() を使用) と、コマンドの実行時にユーザーが最初のパラメーターとして指定したパラメーター (process を使用) を組み合わせる必要があります。 .argv[2 ])それらを接続します。

これらの値への参照は、プロセス オブジェクトの Node.js ドキュメントで見つけることができます。この方法により、ターゲットのディレクトリ/ファイルの絶対パスが取得されます。

ここで、最初に行う必要があるのは、すべての JavaScript 仕様ファイルを見つけることです。 12 行目に示すように、条件演算子を使用して柔軟性を高めることができます。ユーザーが正規ファイルをターゲットとして指定した場合は、ファイル パスを直接使用するだけです。

それ以外の場合、ユーザーがディレクトリ パスを指定した場合は、specRegExp で定義されたパターンに一致するすべてのファイルを検索する必要があります。このパターンは、後で findSpecific 関数を使用して定義します。この関数は、ターゲット ディレクトリ内の各仕様ファイルへのパスの配列を返します。

18 行目では、getspecation() と getassertion() の 2 つの関数を組み合わせて、assertionGroups 定数を定義します。まず各仕様ファイルの内容を取得し、そこからアサーションを抽出します。

これら 2 つの関数は後で定義します。ここでは、最初の関数の出力を 2 番目の関数のパラメーターとして使用することに注意してください。これにより、プロセスが簡略化され、2 つの関数間で直接接続が確立されました。

関数を 1 つだけ持つこともでき、関数を分割することで実際のプロセスが何であるかをよりよく理解できるようになりますが、プログラムは明確で理解しやすいものである必要があり、それだけでは十分ではないことに注意してください。

assertationsGroup 定数は次のように構成されています。

assertionGroup[specification][assertion]

次に、logassertion() 関数を使用して結果をレポートできるように、これらすべてのアサーションをユーザー ログに記録します。各アサーションには結果 (true または false) と簡単な説明が含まれており、この情報を使用して各タイプの結果に特別な色を付けることができます。

最後に、アサーションの結果に基づいて終了コードを定義します。これにより、プログラムがどのように終了したかに関する情報がプロセスに提供されます: プロセスは成功しましたか、それとも失敗しましたか? 終了コード 0 はプロセスが正常に終了したことを意味し、失敗した場合は 1 を意味し、この例では少なくとも 1 つのアサーションが失敗した場合は 1 を意味します。

すべての仕様ファイルを検索

要找到所有的JavaScript规范文件,我们可以使用一个递归函数,该函数遍历用户作为CLI参数指定的目录。在搜索时,应该使用程序开始时定义的正则表达式(/\.spec\.js$/)检查每个文件,该表达式将匹配以.spec.js结尾的所有文件路径。

function findSpecifications (dir, matchPattern) {
  return fs.readdirSync(dir)
    .map(filePath => path.join(dir, filePath))
    .filter(filePath => matchPattern.test(filePath) && fs.statSync(filePath).isFile())
}

我们的findspecification函数接受一个目标目录(dir)和一个正则表达式,该正则表达式标识规范文件(matchPattern)。

获取每个规范的内容

由于我们导出的是模板文本,因此获取内容和计算后的断言非常简单,因此我们必须导入每个文件,当它被导入时,所有的断言都将自动进行计算。

function getSpecifications (paths) {
  return paths.map(path => require(path))
}

使用map()函数,我们使用节点的require函数将数组的路径替换为文件的内容。

从文本中提取断言

此时,我们有一个数组,其中包含每个规范文件的内容,并且已经计算了它们的断言。我们使用旋转门指示器(|-)来查找所有这些断言并提取它们。

function getAssertions (specifications) {
  return specifications.map(specification => ({
    title: specification.split('\n\n', 1)[0].trim(),
    assertions: specification.match(/^( |\t)*(\|-)(.|\n)*?\./gm).map(assertion => {
      const assertionFragments = /(?:\|-) (\w*) ((?:.|\n)*)/.exec(assertion)
 
      return {
        value: assertionFragments[1],
        description: assertionFragments[2].replace(/\n /, '')
      }
    })
  }))
}

这个函数将返回一个类似的数组,但是用一个如下结构的对象替换每个规范的内容:

title: <String: Name of this particular specification>,
  assertions: [
    {
      value: <Boolean: The result of the assertion>,
      description: <String: The short description for the assertion>
    }
  ]
}

标题是用规范字符串的第一行设置的。然后,每个断言都作为数组存储在断言键中。该值将断言的结果表示为布尔值。我们将使用这个值来知道断言是否成功。

此外,描述将显示给用户,作为识别哪些断言成功和哪些断言失败的方法。我们在每种情况下都使用正则表达式。

记录结果

我们沿着程序构建的数组现在有一系列JavaScript规范文件,其中包含一列找到的断言及其结果和描述,因此除了向用户报告结果之外,没有什么可做的。

{
  function logAssertions(assertionGroups) {
  // Methods to log text with colors
  const ansiColor = {
    blue: text => console.log(`\x1b[1m\x1b[34m${text}\x1b[39m\x1b[22m`),
    green: text => console.log(`\x1b[32m    ${text}\x1b[39m`),
    red: text => console.log(`\x1b[31m    ${text}\x1b[39m`)
  }
  // Log the results
  assertionGroups.forEach(group => {
    ansiColor.blue(group.title)
    group.assertions.forEach(assertion => {
      assertion.value === &#39;true&#39;
        ? ansiColor.green(assertion.description)
        : ansiColor.red(assertion.description)
    })
  })
 
  console.log(&#39;\n&#39;)
}

我们可以根据结果使用颜色来格式化输入。为了在终端上显示颜色,我们需要添加ANSI转义码。为了在下一个块中简化它们的用法,我们将每种颜色保存为ansiColor对象的方法。

首先,我们要显示规范的标题,请记住,我们为每个规范使用数组的第一个维度,并将其命名为一组(断言)。然后,我们使用它们各自的颜色根据它们的值记录所有断言:绿色表示计算为true的断言,红色表示具有其他值的断言。

注意比较,我们检查true是否为字符串,因为我们从每个文件接收字符串。

检查结果

最后,最后一步是检查所有测试是否成功。

function checkAssertions (assertionGroups) {
  return assertionGroups.some(
    group => group.assertions.some(assertion => assertion.value === &#39;false&#39;)
  ) ? 1 : 0
}

我们使用数组的some()方法检查每个断言组(规范),看看是否至少有一个值是' ' ' false ' ' '。我们嵌套了其中的两个因为我们有一个二维数组。

运行我们的程序

此时,我们的CLI应准备好运行一些JavaScript规范,并查看是否拾取并评估了断言。在test目录中,您可以从本文开头复制规范示例,并将以下命令粘贴到您的文件中:package.json

"scripts": {
  "test": "node index.js test"
  }

其中test是包含示例规范文件的目录的名称。

当运行npm test命令时,您应该看到使用它们各自颜色的结果。

相关免费学习推荐:js视频教程

更多编程相关知识,请访问:编程入门!!

以上が実行可能な JavaScript 仕様を構築する方法の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsitepoint.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。