正式的编码以前,画个时序图以更好的理清功能: ![](https://img.kancloud.cn/a4/16/a416a43526a68b4e7477617cde106449_721x491.png) 按时序图进行初始化工作如下: ## 添加删除按钮 src/app/student/index/index.component.html ```html <td> <a routerLink="./edit/{{student.id}}" class="btn btn-sm btn-info">编辑</a> <button (click)="onDelete(student)" class="btn btn-sm btn-danger">删除</button> ✚ </td> ``` ### 单元测试 运行单元测试,并将对应的用例由`it`暂时变更为`fit`。 src/app/student/index/index.component.spec.ts ```javascript fit('组件初始化V层渲染', () => { ... expect(table.rows.item(row).cells.item(col++).innerText).toBe('testKlass'); expect(table.rows.item(row).cells.item(col++).innerText).toBe('编辑'); // ✘ expect(table.rows.item(row).cells.item(col++).innerText).toBe('编辑删除'); // ➊ }); ``` * ➊ 增加删除按钮 ## C层添加删除方法 src/app/student/index/index.component.ts ``` /** * 删除学生 * @param student 学生 */ onDelete(student: Student): void { } ``` ### 单元测试 测试点击删除按钮后,是否使用成功的调用了C层的`onDelete`方法 src/app/student/index/index.component.spec.ts ```javascript fit('删除按钮点击测试', () => { // 将C层的onDelete方法设置为替身 // 点击第一行的删除按钮 // 断言onDelete替身被成功调用 }); ``` 补充代码: src/app/student/index/index.component.spec.ts ``` fit('删除按钮点击测试', () => { // 将C层的onDelete方法设置为替身 spyOn(component, 'onDelete'); // 点击第一行的删除按钮 FormTest.clickButton(fixture, 'table button:first-child'); // 断言onDelete替身被成功调用 expect(component.onDelete).toHaveBeenCalledWith(component.pageStudent.content[0]); // ➊ }); ``` * 断言传入了C层中的第一个学生 测试: ![](https://img.kancloud.cn/2a/f0/2af02b11f06a788cfe942bdb83e33c83_652x180.png) 测试反馈说期望的onDelete方法根本就没有被调用过。经几番排查,最终发现这是由于上面的css选择器拼写的不正确造成的,由于在调用`FormTest.clickButton(fixture, 'table button:first-child');`方法时并没有通过对应的css选择器来找到对应的button,所以点击的操作也随之并未完成。`css`选择器书写有误证明了我学业不精,故上述测试代码未按预期发起点击事件本无可厚非,但没有找到按钮却没有任何的提示,就是`FormTest.clickButton`的不对了。 ## 合理的抛出异常 在ts中需要进行异常提示时,使用` throw new Error() `方法。 打开`app -> testing -> FormTest.ts`后仔细回想一下,其实在前面的重构设置`setInputValue`方法时已经使用了抛出异常来代替返回false来处理通过`css`选择器获取到对应的元素的情况了。参考该方法,对`clickButton`改造如下: src/app/testing/FormTest.ts ```javascript static clickButton(fixture: ComponentFixture<any>, cssSelector: string): boolean { const selectorElement = this.getSelectorElement(fixture, cssSelector); if (isNull(selectorElement)) { return false; ✘ throw new Error(`未找到css选器${cssSelector}对应的html元素`); ➊ } const htmlButtonElement: HTMLButtonElement = selectorElement.nativeElement; htmlButtonElement.click(); return true; } ``` * ➊ 使用抛出异常的方法来替代`return false` 此时再看单元测试的提示信息 ![](https://img.kancloud.cn/1d/59/1d590809dd07349d56a8b6c54783d568_477x106.png) ## CSS选择器 怎样使用正确的css选择器来获取删除按钮貌似成为了当前破局的关键点。在没有工具的帮助下,想使用CSS选择器来一次性的获取某个元素并不是一件简单的事情。但有工具就不一样了,下面来展示使用chrome的控制台来快速获取某个html元素选择器的方法: ![](https://img.kancloud.cn/92/4a/924a88acd24707d0099bcf879807350b_1247x438.gif) > [info] 吾尝终日而思矣,不如须臾之所学也;吾尝跂而望矣,不如登高之博见也。登高而招,臂非加长也,而见者远;顺风而呼,声非加疾也,而闻者彰。假舆马者,非利足也,而致千里;假舟楫者,非能水也,而绝江河。君子生非异也,**善假于物也**。 荀子《劝学》 依此方法替换CSS选择器: src/app/student/index/index.component.spec.ts ```javascript fit('删除按钮点击测试', () => { // 将C层的onDelete方法设置为替身 spyOn(component, 'onDelete'); // 点击第一行的删除按钮 FormTest.clickButton(fixture, '#root1 > table > tr:nth-child(2) > td:nth-child(6) > button'); // 断言onDelete替身被成功调用 expect(component.onDelete).toHaveBeenCalledWith(component.pageStudent.content[0]); }); ``` 测试通过。 ## M层添加删除方法 M层添加`deleteById`方法以备调用: src/app/service/student.service.ts ``` /** * 删除学生 * @param id 学生id */ deleteById(id: number) { return null; } ``` 至此初始化工作完成。 # 参考文档 | 名称 | 链接 | 预计学习时长(分) | | --- | --- | --- | | 源码地址 | [https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.8.1](https://github.com/mengyunzhi/spring-boot-and-angular-guild/releases/tag/step4.8.1) | - |