こんにちは、レイモンドです。悪いコードを書いています。 まあ、それほど悪くはありませんが、私は間違いなくすべての「ベストプラクティス」に従っていません。 ただし、1つのプロジェクトが、私が誇りに思うコードの記述を開始するのにどのように役立ったかを説明しましょう。
どういうわけか週末に、私はコンピューターの使用を放棄することにしました。 しかし、それは何ももたらされませんでした。 私はStar Wars APIに出会いました。 このシンプルなインターフェイスはRESTに基づいており、これを使用して、キャラクター、映画、宇宙船などの情報をSWユニバースに要求できます。 検索はしませんが、無料サービスです。
そして、APIで動作するようにJSライブラリをすばやくセットアップしました。 最も単純な場合、同じタイプのすべてのリソースを要求できます。
// swapiModule.getStarships(function(data) { console.log(" getStarships", data); });
または、1つのアイテムを取得します。
// , 2 – swapiModule.getStarship(2,function(data) { console.log(" getStarship/2", data); });
コード自体は1つのファイルに含まれています。そのためにtest.htmlを作成し、GitHubにアップロードしました: github.com/cfjedimaster/SWAPI-Wrapper/tree/v1.0 (これは最初のドラフトです。最終バージョンはこちらです )。
しかし、その後、疑念が私に勝ち始めました。 コード内の何かを改善できますか? ユニットテストを書く必要がありますか? 小さいバージョンを追加しますか?
そして、プロジェクトを改善するためにできることのリストを徐々に作成し始めました。
-コードを記述するときに、その一部が繰り返されたため、最適化が必要でした。 私はこれらのケースを無視し、早すぎる最適化に関与しないようにコードを機能させることに集中しました。 今、戻って最適化を行いたい
-明らかに、単体テストを行う必要があります。 システムはリモートインターフェースで動作し、この場合のテストを行うのはかなり困難ですが、リモートサービスが100%動作することを想定したテストでさえ、テストをまったく行わないよりも優れています。 そして、テストを書くことで、その後のコード変更がプログラムを壊さないことを確認できます
-私はJSHintの大ファンであり 、自分のコードで実行したい
-ライブラリのより小さなバージョンを作成したい-これにはいくつかのコマンドラインユーティリティが役立つと思う
-最後に、GruntやGulpなどのツールを使用して、単体テスト、JSHint検証、およびミニファイを自動的に実行できると確信しています。
そして結果として、JJ BinksよりもJediのように見えるプロジェクトをより自信を持って感じることができます。
単体テストの追加
コードのさまざまな側面が正常に機能することを確認する一連のテストとして想像するのが最も簡単です。 getPeopleとgetPersonの2つの関数を備えたライブラリを想像してください。 それぞれに1つずつ、2つのテストを実行できます。 getPeopleで検索できると想像してください。 検索の3番目のテストを行う必要があります。 getPeopleを使用して、結果をページに分割し、結果を返すページ番号を設定することもできる場合、これについてもテストが必要です。 さて、あなたはポイントを得る。 テストが多いほど、コードを確実に確認できます。
私のライブラリには3種類の呼び出しがあります。 最初はgetResourcesです。 他のAPIエントリポイントのリストを返します。 次に、1つのポジションとすべてのポジションを取得する機会があります。 つまり、惑星にはgetPlanetとgetPlanetsがあります。 ただし、これらの呼び出しはページ分割されたデータを返します。 したがって、APIはgetPlanets(n)の呼び出しもサポートしています(nはページ番号)。 そのため、テストする4つのことがあります。
-getResourcesを呼び出す
-各リソースのgetSingular呼び出し
-各リソースに対してgetPluralを呼び出します
-指定されたページのgetPluralを呼び出します
共通のメソッドが1つあり、リソースごとに3つあります。つまり、テストが必要です。
1 +(3 * number_resources)
6種類のリソースがあり、合計で19のテストがあります。 悪くない。 私のお気に入りのMoment.jsライブラリには 43,399個のテストがあります。
私は、テストにJasmineフレームワークを使用することにしました。 私は彼が好きで、彼を一番よく知っています。 良い点の1つは、ニーズに合わせて変更して開始できるサンプルテストと、テストを実行するためのファイルが用意されていることです。 これは、ライブラリとテストを含むHTMLファイルです。 あなたがそれを開くと、彼はそれらをすべて実行し、結果を表示します。 getResourcesテストから始めました。 ジャスミンに精通していない場合でも、何が起こるかを理解できます。
it(" ", function(done) { swapiModule.getResources(function(data) { expect(data.films).toBeDefined(); expect(data.people).toBeDefined(); expect(data.planets).toBeDefined(); expect(data.species).toBeDefined(); expect(data.starships).toBeDefined(); expect(data.vehicles).toBeDefined(); done(); }); });
getResourcesメソッドは、APIでサポートされる各リソースを表すキーのセットを持つオブジェクトを返します。 したがって、私は単に「このようなキーがなければならない」と言う方法としてtoBeDefinedを使用します。 done()は非同期呼び出し処理に必要です。 次に、他のタイプを検討します。 最初に、リソースから1つのオブジェクトを取得します。
it(" Person", function(done) { swapiModule.getPerson(2,function(person) { var keys = ["birth_year", "created", "edited", "eye_color", "films", "gender", "hair_color", "height", "homeworld", "mass", "name", "skin_color", "species", "starships", "url", "vehicles"]; for(var i=0, len=keys.length; i<len; i++) { expect(person[keys[i]]).toBeDefined(); } done(); }); });
小さな問題があります-識別子2のキャラクターが存在すること、そして彼を記述するキーは変わらないと思います。 しかし、これは怖くない-その場合、テストは簡単に修正できます。 時期尚早のテスト最適化に関与しないでください。
今、群衆の帰還。
it(" People", function(done) { swapiModule.getPeople(function(people) { var keys = ["count", "next", "previous", "results"]; for(var i=0, len=keys.length; i<len; i++) { expect(people[keys[i]]).toBeDefined(); } done(); }); });
2ページ目。
it(" People", function(done) { swapiModule.getPeople(2, function(people) { var keys = ["count", "next", "previous", "results"]; for(var i=0, len=keys.length; i<len; i++) { expect(people[keys[i]]).toBeDefined(); } expect(people.previous).toMatch("page=1"); done(); }); });
実際にはそれだけです。 これで、他の5種類のリソースに対してこれら3つの呼び出しを繰り返すだけで済みます。 テストを書くとき、私はすでにコードに欠陥を見ました。 たとえば、getFilmsは1ページのみを返します。 それ以外は、エラー処理を処理しませんでした。 getFilms(2)リクエストに返すべきものは何ですか? オブジェクト? 例外? まだわかりませんが、後で決めます。
これがテスト結果です。
JSHint Linterの使用
次のステップはリンターを使用することです。 これは、コード品質を評価するためのツールです。 エラーを強調したり、速度の最適化の可能性を示したり、推奨ルールに準拠していないコードを強調したりできます。
当初、JSはJSLintを使用していましたが、私はJSHintの代替を使用しています。 彼はよりリラックスしていて、私もとてもリラックスしているので、彼は私にぴったりです。
お気に入りのエディターなど、JSHintを使用する方法は多数あります。 個人的には、JSHintをサポートする拡張機能があるBracketsを使用します。 しかし、このプロジェクトでは、コマンドラインユーティリティを使用します。 npmがインストールされている場合は、単に言うことができます
npm install -g jshint
その後、コードをテストできます。 例:
jshint swapi.js
ライブラリを減らす
ライブラリは小さく(128行)ですが、明らかに時間の経過とともに減少することはありません。 とにかく、努力する価値がない場合は、なぜそれをしないのですか。 縮小中に、余分なスペースが削除され、変数名が短縮され、ファイルが圧縮されます。 この目的でUglifyJSを選択しました 。
uglifyjs swapi.js -c -m -o swapi.min.js
このツールが未使用のgetResource関数に気づいたのは面白いことです。
// . todo - function getResource(u, cb) { }
合計で、2750バイトのファイルは1397を占有し始めました-約2倍。 2.7 Kb-それほど多くはありませんが、時間の経過とともにライブラリが増加するだけです。
それを自動化する!
とても怠け者なので、このプロセス全体を自動化したいです。 理想的には、これは:
-単体テストを実行します。 成功した場合
-JSHintを削除します。 成功した場合
-ライブラリのミニバージョンを作成する
このために私はGruntを取ります。 これが唯一の選択肢ではなく、 Gulpがまだありますが、私は使用していません。 Gruntを使用すると、一連のタスクを実行できます。タスクの1つが失敗した場合、チェーンを切断できます。 Gruntを使用していない人には、 入門テキストを読むことをお勧めします 。
package.jsonを追加してGruntプラグイン(Jasmine、JSHint、およびUglify)をロードすることにより、次のGruntfile.jsを作成しました。
module.exports = function(grunt) { // grunt.initConfig({ pkg: grunt.file.readJSON('package.json'), uglify: { build: { src: 'lib/swapi.js', dest: 'lib/swapi.min.js' } }, jshint: { all: ['lib/swapi.js'] }, jasmine: { all: { src:"lib/swapi.js", options: { specs:"tests/spec/swapiSpec.js", '--web-security':false } } } }); grunt.loadNpmTasks('grunt-contrib-uglify'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-jasmine'); grunt.registerTask('default', ['jasmine','jshint','uglify']); };
簡単に言えば、すべてのテスト(Jasmine)を実行し、JSHintを実行してからuglifyを実行します。 コマンドプロンプトで「grunt」と入力します。
JSHintを壊すコードを追加するなど、何かを壊すと、Gruntはそれを報告して停止します。
結果は何ですか?
その結果、ライブラリは機能的に変更されていませんが、次のとおりです。
-動作を確認する単体テストがあります。 新しい機能を追加することで、古い機能を壊さないことが確実になります。
-リンターを使用して、推奨事項に従ってコードを確認しました。 外部オブザーバーによるコードの確認は常にプラスです
-ライブラリの縮小を追加しました。 あまり節約しませんでしたが、未来に影響を与えました
-このキッチン全体を自動化しました。 これはすべて、1つの小さなチームで行うことができます。 人生は美しく、私は超忍者のコードになりました。
今、私のプロジェクトは良くなり、気に入っています。 最終版はこちらからダウンロードできます 。