【问题标题】:expect(component).toBeTruthy() failsexpect(component).toBeTruthy() 失败
【发布时间】:2019-07-23 11:54:40
【问题描述】:

我想为一个组件编写单元测试。第一次测试失败, 出现此错误:

错误:预期未定义是真实的。

it 块输出错误:

it('should create', () => {
    expect(component).toBeTruthy();
  });

登录模板:

<h3>Login</h3>
<form class="form form-group" (ngSubmit)="onSubmit()">
  <div class="row">
    <label for="email" class="login-form-label col-4">Email:</label>
    <input ngModel [(ngModel)]="email" name="email" (ngModelChange)="validateEmail()" type="email" id="email" class="col-3 form-control">
    <span class="error col-sm-4">{{ this.emailErr }}</span>
  </div>
  <br>
  <div class="row">
    <label for="password" class="login-form-label col-4">Wachtwoord:</label>
    <input ngModel [(ngModel)]="password" name="password" (ngModelChange)="validatePassword()" type="password" id="password" class="col-3 form-control">
    <span class="error col-sm-4">{{ this.passwordErr }}</span>
  </div>
  <input type="submit" [disabled]="!isValid()" value="Login" class="login-button col-1">
</form>

我试过了:

describe('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ LoginComponent ],
      providers: [
        LoginComponent,
        { provide: RoutingService, useValue: MockRoutingService },
        { provide: AuthenticationService, useValue: MockAuthenticationService }
      ]
    })
    .compileComponents();
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
  });

还有:

describe('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ LoginComponent ],
      providers: [
        LoginComponent,
        { provide: RoutingService, useValue: MockRoutingService },
        { provide: AuthenticationService, useValue: MockAuthenticationService }
      ]
    })
    .compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

还有:

describe('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ LoginComponent ],
      providers: [
        LoginComponent,
        { provide: RoutingService, useValue: MockRoutingService },
        { provide: AuthenticationService, useValue: MockAuthenticationService }
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

LoginComponent:

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  private password = '';
  private email = '';
  private emailErr = '';
  private passwordErr = '';

  constructor(private authService: AuthenticationService, private routingService: RoutingService) { }

  ngOnInit() {
  }

  onSubmit() {
    this.emailErr = '';
    this.passwordErr = '';

    const response: { ['emailValid']: boolean, ['passwordValid']: boolean } = this.authService.login(this.email, this.password);

    const self = this;
    setTimeout(() => {
      if (response.emailValid === false) {
        self.emailErr = '* Your username was incorrect, please try again.';
        return;
      } else if (response.passwordValid === false) {
        self.passwordErr = '* Your password was incorrect, please try again.';
        return;
      }
      self.routingService.route('home');
    }, 300);
  }

  validateEmail() {
    this.emailErr = '';
    if (this.email === '') {
      this.emailErr = '* Please enter your email.';
    }
  }

  validatePassword() {
    this.passwordErr = '';
    if (this.password === '') {
      this.passwordErr = '* Please enter your password.';
    }
  }

  isValid() {
    if (this.password === '' || this.email === '') {
      return false;
    } else if (this.emailErr !== '' || this.passwordErr !== '') {
      return false;
    }
    return true;
  }

}

我在这里错过了什么?

【问题讨论】:

  • 尝试减少代码示例并提出更具体的问题。这是很多要审查的来源
  • @Gnqz 我减少了它,但不确定我是否应该将登录组件保留在帖子中。
  • 错误是在第一个 it 块内断言组件已定义?正如在下面的答案中所说,您肯定需要从提供程序列表中删除 LoginComponent 并在声明您的 TestBed 配置的 beforeEach 中使用 async 。您能否分享失败的规范中的测试用例(it 块)和您的 LoginComponent 模板(html)?
  • @Erbsenkoenig 刚刚更新了帖子,希望对您有所帮助

标签: angular unit-testing


【解决方案1】:

正如 nash11 所指出的,您肯定需要从提供程序列表中删除 LoginComponent,第一个 beforeEach 应该运行 async

我希望这样做您实际上应该收到一条不同的消息,告诉您 ngModel 不是 &lt;input/&gt; 的已知属性

有两种方法可以让您的测试正常工作,具体取决于您是否希望 [{ngModel}] 像通常那样工作,或者您是否只想对您不感兴趣的应用程序进行浅层测试 [{ngModel}] 实际上作品。

因此,如果您希望 ngModel 正常工作,则需要将 FormsModule 导入您的 TestBed。

如果您不介意 ngModel 可以工作,但它只是一个存在的属性,您可以在 TestBed 中设置 NO_ERRORS_SCHEMA

describe('AppComponent', () => {
    let component: AppComponent;
    let fixture: ComponentFixture<AppComponent>;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [AppComponent],
            providers: [
              { provide: RoutingService, useValue: MockRoutingService },
              { provide: AuthenticationService, useValue: MockAuthenticationService }],
            // imports: [FormsModule] // import the FormsModule if you want ngModel to be working inside the test
            schemas: [NO_ERRORS_SCHEMA] // remove the FormsModule import and use that schema to only shallow test your component. Please refer to the official document for more information.
        })
            .compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(AppComponent);
        component = fixture.componentInstance;
    });

    it('should be created', () => {
      fixture.detectChanges();
      expect(component).toBeTruthy();
    });
});

这是一个有效的stackblitz

【讨论】:

    【解决方案2】:

    LoginComponent 不是provider。将其添加到声明中就足够了。尝试从您的 providers 中删除 LoginComponent 并再次运行测试。

    我会选择你的第三个选项。 async 可以让所有异步代码在继续之前完成。

    describe('LoginComponent', () => {
      let component: LoginComponent;
      let fixture: ComponentFixture<LoginComponent>;
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          declarations: [ LoginComponent ],
          providers: [
            { provide: RoutingService, useValue: MockRoutingService },
            { provide: AuthenticationService, useValue: MockAuthenticationService }
          ]
        })
        .compileComponents();
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(LoginComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });
    

    【讨论】:

    • 不幸的是,这导致了完全相同的错误
    猜你喜欢
    • 2019-12-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-14
    • 2021-09-24
    • 2016-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多