在TypeScript + Angular1项目中编写单元测试
在准备给基于Angular1的产品进行测试时,需要进行环境设置和实际测试代码的编写等工作。我将把这些内容整理在一起。
前提条件
-
- TypeScript(v1.8, 含テストコード)
-
- AngularJS(v1.4)
- karma+mocha+power-assert
代码不是采用现代的require依赖解析并进行bundle的方式,而是使用TypeScript的命名空间来进行范围划分,最后将其合并为一个单一文件并输出为tsc –outFile dist/bundle.js的形式。
在以下链接的GitHub存储库中,重现了环境:https://github.com/sisisin-sandbox/ngts/tree/master/namespace-style
使最低要求karma得以操作
在不同的选项中尝试过许多,但最终我决定参考这篇文章。
-
- プロダクトコードはconcat済のものを参照(karma.conf.jsのfilesにはdist/bundle.jsを読ませる)
- テストコードはkarma-browserifyを利用し、browserify + tsify + espowerifyでts -> espoweredなjsにして実行
在参考产品代码的同时编写测试代码,并在断言错误时能够详细查看power-assert的错误提示,我们得到了这样的结果。
用karma运行Directive的测试。
当以Directive使用templateUrl引用html时,可能会出现以下错误,并导致测试失败。
Error: Unexpected request: GET /views/appHelloDirective.html
No more request expected
at $httpBackend (/var/folders/0q/d6gvmfsn7gv849kw__r2py_00000gp/T/node_modules/angular-mocks/angular-mocks.js:1418:0 <- /var/folders/0q/d6gvmfsn7gv849kw__r2py_00000gp/T/cd8d66ce58eb2b84e246e72fab23a78f.browserify:1727:9)
at sendReq (node_modules/angular/angular.js:11776:9)
at serverRequest (node_modules/angular/angular.js:11571:16)
at processQueue (node_modules/angular/angular.js:16383:28)
at node_modules/angular/angular.js:16399:27
at Scope.$eval (node_modules/angular/angular.js:17682:28)
at Scope.$digest (node_modules/angular/angular.js:17495:31)
at Context.<anonymous> (/var/folders/0q/d6gvmfsn7gv849kw__r2py_00000gp/T/test/directive-spec.ts:25:10 <- /var/folders/0q/d6gvmfsn7gv849kw__r2py_00000gp/T/cd8d66ce58eb2b84e246e72fab23a78f.browserify:17408:15)
为了避免这个问题,我们可以使用karma-ng-html2js-preprocessor这个包。
这个包可以将以Angular模板编写的html文件转换为js文件,并且可以适应karma的执行。
另外,还提供了解决templateUrl文件路径的选项,即使运行时的路径与当前目录不同,也可以灵活处理。
写测试时的方针。
总体方针
-
- Angularに依存している部分はanuglar-mocksでモックしつつテストする
Directive,$scope,$httpなど
DI対象が多い場合やネストしてる場合もangular-mocksを利用してやる
それ以外はなるべくangular-mocksを利用しないでjs(ts)オンリーでテストを書く
薄いControllerや$httpを利用しないService,Factoryなど
テスト用のデータは関数で取得するようにする
例:const data1 = () => [1, 2, 3];
生のオブジェクトや配列を$scopeなどに渡すとAngularの方でプロパティ生やされたりするので($$hashKeyなど)
指令
-
- 極力attributeから渡された値がControllerにbindされていることを確認するのみに留める
link関数などで盛大にロジックが書かれてしまっている場合
一旦ロジックに対してもテストを書く
しかる後にロジックをテストとともにControllerに分離してbindToControllerに書き直し、link関数を撲滅する
基本上,我认为如果按照@armorik83先生的现代实践和@laco0416先生的AngularJS老化检查编写方法,你就能够确保可测试性。
课题(或者说尚未清晰可见的事物)
compileやlink関数内で動的に出力するtemplateを差し替えていたりする場合のテストをどうするか
控制器
$scopeなどに依存してない部分は普通にテスト書く
$scope.$watchなどに依存している場合
$watchに登録したコールバック関数の処理が単純なら振る舞いをそのままテストする
複雑な場合は、コールバック関数をプロパティなどにして、$watchの発火のテストとコールバック関数についてのテストを分割する(この辺参照)
ここでarrow function使っておかないとthisの扱いで爆死するので注意
そもそもそんな複雑なことやるなという話
テストを書いたら、$scope依存は消せそうか検討して極力消す。
$scopeのプロパティを利用していた場合
全てControllerのプロパティへ移植する
Directiveに紐づいていた場合はbindToControllerを利用
服务,工厂
$http, $resoucesに依存している場合
$httpBackendを利用してAPIレスポンスをモックしてテスト
それ以外は普通にテスト書くだけ(多分
过滤
只是一个普通函数,所以我认为没有太多需要做特别的事情。
最后
因此,在TypeScript + Angular1系环境中,我简单地总结了如何构建单元测试环境以及编写测试代码的方针。由于我只是在我目前涉及的项目中进行编写,所以可能不是十分全面,但希望能对您有所帮助。