我们说在应用中,我们需要一个依赖于应用状态和本地存储的服务用来通过一个rest API来执行服务端存储,当我们测试我们的controller时,我们不必和服务端进行通信,毕竟只是在测试controller而已。我们仅添加一个与我们最初组件相同的mock服务,注入器能够确保我们的controller获得一个虚拟的服务,controller自身不必也不需要了解这种差异。
那么说说测试吧。
4.以测试驱动的开发
这部分是一个架构的第三部分,但是他是很重要的,以至于我需要将它放在最重要的位置。
在我们所有见过的,用过的以及写过的jQuery插件中,有多少具有一套测试组件呢?其实并不多,这是因为jQuery在测试上不易控制,但是AngularJS却与此不同。
在jQuery中,测试的唯一方法是使用一个demo页去创建一个独立组件来使得我们的测试可以执行dom操作。我们接下来我们必须开发一个独立的组件然后将它集成到我们的应用中来,这是多不方便啊!在很多情况下,当我们使用jQuery开发实际上是做了很多重复开发而不是以测试驱动的开发,这又能怪我们吗?
但是在AngularJS中我们可以关注分离点,所以我们可以做一些测试驱动的开发。例如,我们有一个directive用来说明在menu中我们的当前路径,我们可以在视图中这样声明:
复制代码 代码如下:
<a href="https://www.jb51.net/hello" when-active>Hello</a>
好了,现在我们可以写一个测试用来测试这个不存在的指令when-active了
复制代码 代码如下:
it( 'should add "active" when the route changes', inject(function() {
var elm = $compile( '<a href="https://www.jb51.net/hello" when-active>Hello</a>' )( $scope );
$location.path('/not-matching');
expect( elm.hasClass('active') ).toBeFalsey();
$location.path( 'https://www.jb51.net/hello' );
expect( elm.hasClass('active') ).toBeTruthy();
}));
我们直接run测试用例,你会发现是失败的,这时候需要创建这个指令,如下:
复制代码 代码如下:
.directive( 'whenActive', function ( $location ) {
return {
scope: true,
link: function ( scope, element, attrs ) {
scope.$on( '$routeChangeSuccess', function () {
if ( $location.path() == element.attr( 'href' ) ) {
element.addClass( 'active' );
}
else {
element.removeClass( 'active' );
}
});
}
};
});
再次run这个测试用例,你会发现通过了并且菜单如请求的样子显示,我们的开发是兼有反复性和可测试性,非常酷吧!
5.从概念上讲,指令不是打包的jQuery
你常常听说,dom操作只能在指令中,这是必须的,你必须严肃对待。
让我们深入讨论,
某些指令仅仅是装饰我们的视图(例如ngClass),因此有时候直接操作dom是可以的,但是当一个指令类似于一个小物件并且拥有自己的模板,那么它应该当做一个分离的关注点,这就是说,它的模板需要和link中的执行逻辑以及其他controller函数分离开。
AngularJS拥有一整套的工具可以是这种分离更简单,使用ngClass指令,我们可以动态地更新class,使用ngBind我们可以进行双向数据绑定,使用ngShow和ngHide 我们
可以采用编程的形式显示和隐藏一个元素,也包括我们自己写的很多指令。换句话说,我们可以不用Dom操作而完成所有工作,dom操作越少,指令越容易测试,越容易指定他们的style属性,就越容易在将来改变他们,那么他们就越容易复用和分发。