茉莉花 - 无法读取未定义的属性"控件" - 角度 6



我是Angular的初学者,正在用Jasmine为我的Angular6EmployeeComponent编写一个测试。我收到一个错误

无法读取未定义的属性"controls">

我想我没有在employee.component.spec.ts文件中正确设置所有依赖项。我研究了几天,但没有结果。任何帮助都将不胜感激。

这是我的代码中的基本流程。员工组件包含一个注册表。当用户单击Submit时,员工服务会将数据插入/更新到firebase。

下面是employee.component.html

<div class="row">
<div class="col-md-5">
<form [formGroup]="this.employeeService.form" (ngSubmit)="onSubmit()">
<input type="hidden" formControlName="$key">
<div class="form-group" *ngIf="page1">
<label>Full Name</label>
<input formControlName="fullName" class="form-control" [ngClass]="{'is-invalid': submitted && formControls.fullName.errors}">
<div class="invalid-feedback" *ngIf="submitted && formControls.fullName.errors">
This field is required.
</div>
</div>
<div class="form-group" *ngIf="page1">
<label>Email</label>
<input formControlName="email" class="form-control" [ngClass]="{'is-invalid': submitted && formControls.email.errors}">
<div class="invalid-feedback" *ngIf="submitted && formControls.email.errors">
Invalid Email Address.
</div>
</div>
<div class="form-group" *ngIf="page1">
<label>Mobile</label>
<input formControlName="mobile" class="form-control" [ngClass]="{'is-invalid': submitted && formControls.mobile.errors}">
<div class="invalid-feedback" *ngIf="submitted && formControls.mobile.errors">
<label *ngIf="formControls.mobile.errors.required">This field is required.</label>
<label *ngIf="formControls.mobile.errors.minLength">At least 8 characters</label>
</div>
</div>
<div style="text-align: right" *ngIf="page1">
<button class="btn btn-primary" (click)="part1Next()">Next</button>
</div>
<div class="form-group" *ngIf="page2">
<label>School</label>
<input formControlName="school" class="form-control">
</div>
<!-- <div class="form-group" *ngIf="page2">
<label>Degree</label>
<input formControlName="degree" class="form-control">
</div> -->
<div class="form-group" *ngIf="page2">
<label>Degree</label>
<select class="form-control" formControlName="degree">
<option value="bachelor">Bachelor</option>
<option value="master">Master</option>
</select>
</div>
<div class="form-group" *ngIf="page2">
<label>Location</label>
<input formControlName="location" class="form-control">
</div>
<div class="row">
<div class="col-md-6" style="text-align: center" *ngIf="page2">
<button class="btn btn-primary" (click)="part2Back()">Back</button>
</div>
<div class="col-md-6" style="text-align: center" *ngIf="page2">
<button class="btn btn-primary" (click)="part2Next()">Next</button>
</div>
</div>
<div *ngIf="page3">
<div>
<h2>Please Check all of the information below</h2>
<p>Full Name: {{formControls.fullName.value}}</p>
<p>Email: {{formControls.email.value}}</p>
<p>Mobile: {{formControls.mobile.value}}</p>
<p>School: {{formControls.school.value}}</p>
<p>Degree: {{formControls.degree.value}}</p>
<p>Location: {{formControls.location.value}}</p>
</div>
<div class="row">
<div class="col-md-6" style="text-align: center">
<button class="btn btn-primary" (click)="part3Back()">Back</button>
</div>
<div class="col-md-6" style="text-align: center" class="form-group">
<input type="submit" class = "btn btn-primary" value="Submit">
</div>
</div>
</div>
</form>
<div class="alert alert-info" *ngIf="showSuccessMessage">
Submitted Successfully
</div>
</div>
<div class="col-md-7">
<!-- <app-employee-list></app-employee-list> -->
</div>
</div>

以下是employee.component.ts

import { Component, OnInit } from '@angular/core';
import { EmployeeService } from '../shared/employee.service';
@Component({
selector: 'app-employee',
templateUrl: './employee.component.html',
styleUrls: ['./employee.component.scss']
})
export class EmployeeComponent implements OnInit {
constructor(private employeeService: EmployeeService) { }
page1: boolean = true;
page2: boolean = false;
page3: boolean = false;
part1Next() {
this.page1 = false;
this.page2 = true;
}
part2Back() {
this.page1 = true;
this.page2 = false;
}
part2Next() {
this.page2 = false;
this.page3 = true;
}
part3Back() {
this.page2 = true;
this.page3 = false;
}
submitted: boolean;
showSuccessMessage: boolean;
formControls = this.employeeService.form.controls;
ngOnInit() {
}
onSubmit() {
this.submitted = true;
if (this.employeeService.form.valid) {
if (this.employeeService.form.get('$key').value == null) {
// insert
this.employeeService.insertEmployee(this.employeeService.form.value);
} else {
// update
this.employeeService.updateEmployee(this.employeeService.form.value);
}
this.showSuccessMessage = true;
setTimeout(() => this.showSuccessMessage = false, 2000);
this.submitted = false;
this.employeeService.form.reset();
}
this.page1 = true;
this.page2 = false;
this.page3 = false;
}
}

以下是employee.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { EmployeeComponent } from './employee.component';
import { FormsModule } from '@angular/forms';
import { AngularFireDatabase, AngularFireList } from 'angularfire2/database';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { EmployeeService } from '../shared/employee.service';
class MockEmpService { 
employeeList: AngularFireList<any>;
form = new FormGroup({
$key: new FormControl(null),
fullName: new FormControl('', Validators.required),
email: new FormControl('', Validators.email),
mobile: new FormControl('', [Validators.required, Validators.minLength(8)]),
school: new FormControl(''),
degree: new FormControl(''),
location: new FormControl('')
});
insertEmployee() {
return true;
}
updateEmployee() {
return true;
}
}
fdescribe('EmployeeComponent', () => {
let component: EmployeeComponent;
let fixture: ComponentFixture<EmployeeComponent>;
let empService: MockEmpService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ EmployeeComponent ],
imports: [ ReactiveFormsModule, FormsModule ],
providers: [ 
{provide: EmployeeService, useValue: MockEmpService},  
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EmployeeComponent);
component = fixture.componentInstance;
empService = TestBed.get(MockEmpService);
fixture.detectChanges();
});
// first test
// when onSubmit() from component is called, insert() or update() from service is called
it('calling insert or update from service when onSubmit is called', () => {
spyOn(component, 'onSubmit');
expect(empService.insertEmployee).toHaveBeenCalled();
});
// Test created automatically
// it('should create', () => {
//   expect(component).toBeTruthy();
// });
});

以下是employee.service.ts

import { Injectable } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { AngularFireDatabase, AngularFireList } from 'angularfire2/database';
@Injectable({
providedIn: 'root'
})
export class EmployeeService {
employeeList: AngularFireList<any>;
constructor(private firebase: AngularFireDatabase) { }
form = new FormGroup({
$key: new FormControl(null),
fullName: new FormControl('', Validators.required),
email: new FormControl('', Validators.email),
mobile: new FormControl('', [Validators.required, Validators.minLength(8)]),
school: new FormControl(''),
degree: new FormControl(''),
location: new FormControl('')
});
getCustomers() {
this.employeeList = this.firebase.list('employees');
console.log(this.employeeList);
return this.employeeList.snapshotChanges();
}
insertEmployee(employee) {
this.employeeList.push({
fullName: employee.fullName,
email: employee.email,
mobile: employee.mobile,
school: employee.school,
degree: employee.degree,
location: employee.location
});
}
updateEmployee(employee) {
this.employeeList.update(employee.$key,
{
fullName: employee.fullName,
email: employee.email,
mobile: employee.mobile,
school: employee.school,
degree: employee.degree,
location: employee.location
});
}
populateForm(employee) {
this.form.setValue(employee);
}
deleteEmployee($key: string) {
this.employeeList.remove($key);
}
}

Anh,欢迎使用StackOverflow。:(首先,这是你第一次写一个非常彻底的问题。干得好!

因为它是如此彻底,我能够组装一个堆叠的litz来测试你遇到了什么。你可以在这里找到它:Jasmine-无法读取未定义的属性"controls"-Angular 6

正如您在stackblitz中看到的,您的测试现在通过了。以下是我所做的工作:

  • 将EmployeeService的提供程序行更改为useClass而不是useValue-您正在用一个类替换另一个类
  • 在"let"声明和"TestBed.get(EmployeeService("调用中将empService的类型更改为EmployeyService。原因是您正在用不同的类模拟EmployeeService,但就组件而言,它仍然使用原始名称
  • 评论了这行:spyOn(component, 'onSubmit');,因为这会使你试图测试的函数失效
  • 在empService.insertEmployee((函数上创建了insertSpy到spy
  • 添加了行let formValidSpy = spyOnProperty(empService.form, 'valid', 'get').and.returnValue(true);,因为您需要模拟被设置为有效或无效的表单来控制onSubmit((的哪些部分被执行
  • 最后实际调用函数,用component.onSubmit();进行测试

有关所有详细信息,请参阅stackblitz。我希望这能有所帮助。

最新更新