在前面小节中我们使用`ng t`在单元测试的支撑下,完成了单一模块下的单一组件开发。而组件最终仍然是要集成到整个系统中来运行的,所以组件完成后的集成测试也是必要的一环。 在项目根目录下使用`ng s`来启动项目,并打开 [http://localhost:4200/](http://localhost:4200/)来查看项目。 此时将在shell控制台中得到如下错语提示: ```bash Error: app.component.html:15:22 - error TS2339: Property 'name' does not exist on type 'never'. 15 <td>{{ teacher.name }}</td> ~~~~ app.component.ts:6:16 6 templateUrl: './app.component.html', ~~~~~~~~~~~~~~~~~~~~~~ Error occurs in the template of component AppComponent. ... ``` >[success] 遇到错误的时候,第一件事情应该是翻译错误提示、尝试理解错误提示。 上述提示大体在说:由于在类型`never`并不存在`name`属性,所以我们在V层上使用 `{{ teacher.name }}`是不被允许的。 这也说明了`ng serve`比`ng t`多了模板语法的检查。 ## 强类型 当前错误产生的根本原因是我们当前所使用TypeScript是个强类型语言,这要求每个变量都应该有类型,而且应该有正确的类型。 而我们在C层文件中,如下声明了`teachers`的类型: ```bash // 初始化教师数组 teachers = []; ``` 即`teachers`的类型是个数组,而至于该数组中的第一项的类型是什么,我们并未说明。对应的V层文件: ```html <tr *ngFor="let teacher of teachers; index as i"> ❶ <td>{{ i + 1 }}</td> <td>{{ teacher.name }}</td>❷ ``` * ❶ 执行ngFor的前提是teachers是个数组,而C层声明正是数组,符合条件不报错。 * ❷ 只有当数据中的数据中存在name时,才能够获取到name。而当前**未**声明数组中的数据类型,typescript中如果未对数据类型进行声明,则会默认其数据类型为`never`,表示:什么都不是。 解决此问题的方法即是为teachers中的数据项声明类型,在此我们将其声明为`any`类型,任**任意类型**,可以理解为:你想要的本类型都有。 >[info] never与any是个类型上的反义词。never:本类型上什么都属性都没有,要什么没什么;any:本类型上什么属性都有,要什么有什么。 ```typescript // 初始化教师数组 teachers = [] as Array<any>; ``` 或者: ```typescript // 初始化教师数组 teachers = new Array<any>(); ``` 或者: ```typescript // 初始化教师数组 teachers = [] as any[]; ``` 此时再次对`teachers`进行循环时,编译器得知循环出的对象的类型是any,该类型上什么属性都有,当然也必然有name属性而不报错了。 ## 控制台 在教程中提到控制台基本上分为两种:shell控制台以及chrome控制台,在开发过程中时刻关注控制台是个要保持的好习惯。 虽然在shell控制台已然看不到任何报错信息,但[http://localhost:4200/](http://localhost:4200/)中却查看不到任何内容,此时便应该去chrome控制台中查看到底发生了什么。 ![](https://img.kancloud.cn/c8/26/c8264d146d487018769642f2fb0ec5bb_1021x128.png) 报错信息似曾相识,没错这就是那个由于未成功完成依赖注入时Angular给我们反馈的错误。但是我们明明已经在前面的开发中解决了这个问题,此时为何又出现了呢? 这时候便不得不重提下Angular的单元测试与模块化设计了。 ## ng t与ng s 与大多数的应用相同,运行环境(ng s)是看不到测试环境(ng t)的。这里的环境当前即指为了运行App组件而做的准备工作。 所以无论我们在测试环境中设置什么样的内容,运行环境都无从感知。这当然包含我们在测试环境中为App组件引入的那个可提供`HttpClient`的`HttpClientModule`。 在`ng t`启动的测试环境中,由`app.component.spec.ts`声明了`DynamicTestModule`,该动态测试模块提供了运行App组件的基本能力: ```bash ├── app-routing.module.ts ├── app.component.css 😀 ├── app.component.html 😀 ├── app.component.spec.ts 😀 ├── app.component.ts 😀 └── app.module.ts ``` * `ng t`时,上述4个文件被关联到DynamicTestModule中 而在`ng s`启动的运行环境中,由`app.module.ts`声明了`AppModule`模块,该App模块提供了运行App组件的基本能力: ```bash ├── app-routing.module.ts ├── app.component.css 😀 ├── app.component.html 😀 ├── app.component.spec.ts ├── app.component.ts 😀 └── app.module.ts 😀 ``` * `ng s`时,上述4个文件被关联到AppModule中 ```typescript @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } ``` 如上述代码所示`AppModule`并没有引入那个可提供`HttpClient`的`HttpClientModule`。 问题的根本原因弄明白后,解决该问题也就简单了: ```typescript import {HttpClientModule} from '@angular/common/http'; ✚ @NgModule({ declarations: [ AppComponent ], imports: [ BrowserModule, AppRoutingModule, HttpClientModule ✚ ], providers: [], bootstrap: [AppComponent] }) export class AppModule { } ``` ![](https://img.kancloud.cn/ee/16/ee163a3b968e08e068518a61a28e0d00_549x206.png) >[success] spec.ts文件仅在`ng t`下生效,仅用于单元测试模式,运行环境将完全无视该类型文件。