简体   繁体   English

角度测试错误 - 无法绑定到“项目”,因为它不是“app-dropdown”的已知属性

[英]Angular Test error — Can't bind to 'items' since it isn't a known property of 'app-dropdown'

Just want to say that I recognize that there are many SO posts related to "Can't bind to X since it isn't a known property of Y" errors. 只是想说我认识到有很多SO帖子与“无法绑定到X,因为它不是Y的已知属性”错误有关。 I've looked at a ton of them, and have found a number of answers which solve the specific problems, but which I've had trouble translating to my case, which I actually think is quite general, and relates to a fundamental misunderstanding of how I should be solving my use case. 我已经看了很多,并找到了一些解决具体问题的答案,但是我在翻译到我的案例时遇到了麻烦,我认为这个问题很普遍,并且与一个基本的误解有关。我应该如何解决我的用例。

I'm creating an Angular (7) app, which I've separated into components and routes . 我正在创建一个Angular(7)应用程序,我将其分为componentsroutes The components are modular pieces (dropdowns, modals, buttons, whatever), and the routes are individual pages in the app. components是模块化部件(下拉菜单,模态,按钮等),路径是应用程序中的单个页面。 The terming is a little convoluted, because both are technically Angular components. 这个问题有点复杂,因为两者都是技术上的角度组件。 In other words, the structure (within src/ ) looks like this: 换句话说,结构(在src/ )看起来像这样:

- app
  - components
    - dropdown
      - dropdown.component.ts
      - dropdown.component.html
      - dropdown.component.scss
      - dropdown.component.spec.ts
  - routes
    - library
      - library.component.ts
      - library.component.html
      - library.component.scss
      - library.component.spec.ts
  ...

So I have a Library route, which is just an Angular component which imports a Dropdown component and looks like this: 所以我有一个Library路由,它只是一个导入Dropdown组件的Angular组件,如下所示:

import { Component, OnInit } from '@angular/core';

import { DropdownComponent } from '../../components/dropdown/dropdown.component';

@Component({
  selector: 'app-library',
  templateUrl: './library.component.html',
  styleUrls: ['./library.component.scss']
})
export class LibraryComponent implements OnInit {
  pickItem($event) {
    console.log($event.item, $event.index);
  }

  constructor() { }

  ngOnInit() {}
}

The relevant Library HTML file: 相关的库HTML文件:

<div class="library row py4">
  <h3 class="typ--geo-bold typ--caps mb5">Style Library</h3>

  <div class="mb4" style="max-width: 35rem;">
    <p class="typ--geo-bold typ--caps">Dropdowns</p>
    <app-dropdown
      [items]="['One', 'Two', 'Three']"
      (pick)="pickItem($event)"
      title="Default dropdown"
    ></app-dropdown>
    <br />
    <app-dropdown
      [items]="['One', 'Two', 'Three']"
      (pick)="pickItem($event)"
      title="Inline dropdown"
      class="dropdown--inline"
    ></app-dropdown>
  </div>
</div>

The dropdown component is a basic component, following a similar structure. 下拉组件是一个基本组件,遵循类似的结构。 I won't paste it here unless asked, because I'm not sure it'd be additive. 除非被问到,否则我不会将其粘贴在此处,因为我不确定它是否会添加。 (Suffice it to say that it does accept items as an Input -- relevant to the below error). (只需说它确实接受items作为输入 - 与以下错误相关)。

This works perfectly in the browser, and builds correctly in production. 这在浏览器中完美运行,并在生产中正确构建。

When I run my library.components.spec.ts test, though, I run into the following error: 但是,当我运行我的library.components.spec.ts测试时,我遇到以下错误:

Failed: Template parse errors:
Can't bind to 'items' since it isn't a known property of 'app-dropdown'.
1. If 'app-dropdown' is an Angular component and it has 'items' input, then verify that it is part of this module.
2. If 'app-dropdown' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component. ("
    <p class="typ--geo-bold typ--caps">Dropdowns</p>
    <app-dropdown
      [ERROR ->][items]="['One', 'Two', 'Three']"
      (pick)="pickItem($event)"
      title="Default dropdown"

Here's the basic Library spec file: 这是基本的库规范文件:

import { TestBed, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { LibraryComponent } from './library.component';

describe('LibraryComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      declarations: [LibraryComponent],
    }).compileComponents();
  }));

  it('should create the component', () => {
    const fixture = TestBed.createComponent(LibraryComponent);
    const lib = fixture.debugElement.componentInstance;
    expect(lib).toBeTruthy();
  });
});

Neither the Library component nor the Dropdown component have associated modules. Library组件和Dropdown组件都没有关联的模块。 My understanding is that this error may relate to the fact that I haven't imported the Dropdown component into the Library module, or something, but 我的理解是,这个错误可能与我没有将Dropdown组件导入Library模块或其他东西的事实有关

  • A) I'm not sure how to do that, A)我不知道该怎么做,
  • B) I'm then not sure how to incorporate that module into the app at large, and B)我当时不确定如何将该模块整合到应用程序中,并且
  • C) I'm not sure what the value of that is, outside of getting the test to work. C)除了让测试工作之外,我不确定它的价值是什么。

Is there a way to get the test to work without converting these components to modules? 有没有办法让测试工作而不将这些组件转换为模块? Should all route components be modules? 所有路由组件都应该是模块吗?

EDIT 编辑

Adding The dropdown component, if relevant: 添加下拉组件(如果相关):

<div
  class="dropdown"
  [ngClass]="{ open: open }"
  tab-index="-1"
  [class]="class"
  #dropdown
>
  <div class="dropdown__toggle" (click)="onTitleClick(dropdown)">
    <span class="dropdown__title">{{ finalTitle() }}</span>
    <span class="dropdown__icon icon-arrow-down"></span>
  </div>
  <div *ngIf="open" class="dropdown__menu">
    <ul>
      <li
        *ngFor="let item of items; let ind = index"
        class="dropdown__item"
        [ngClass]="{ selected: selectedIndex === ind }"
        (click)="onItemClick(dropdown, item, ind)"
      >
        <span class="dropdown__label">{{ item }}</span>
      </li>
    </ul>
  </div>
</div>

And: 和:

import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';

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

  @Input() items: Array<string>;
  @Input() public selectedIndex: number = null;
  @Input() public title = 'Select one';
  @Input() public open = false;
  @Input() public class = '';

  @Output() pick = new EventEmitter();

  constructor() { }

  ngOnInit() {}

  finalTitle () {
    return typeof this.selectedIndex === 'number'
      ? this.items[this.selectedIndex]
      : this.title;
  }

  onItemClick(dropdown, item, index) {
    this._blur(dropdown);
    this.selectedIndex = index;
    this.open = false;
    this.pick.emit({ item, index });
  }

  onTitleClick(dropdown) {
    this._blur(dropdown);
    this.open = !this.open;
  }

  _blur(dropdown) {
    if (dropdown && dropdown.blur) { dropdown.blur(); }
  }
}

Updated to more clearly answer your 3 questions 更新以更清楚地回答您的3个问题

A) There are many ways to accomplish this without creating new modules. A)有许多方法可以在不创建新模块的情况下实现此目的。 You could simply import the DropdownComponent into your test module as André suggests below. 你可以简单地将DropdownComponent导入你的测试模块,正如André在下面所建议的那样。 I outline another method below in this answer that stubs the DropdownComponent, just for the purpose of testing LibraryComponent. 我在下面的答案中概述了另一个方法,该方法存根DropdownComponent,仅用于测试LibraryComponent。 Hopefully this answers the question of "how to do that". 希望这能回答“如何做到这一点”的问题。

B) The stub I suggest isn't a module - it is barely even a component. B)我建议的存根不是一个模块 - 它几乎不是一个组件。 There is no need to incorporate the stub into the app at large, it's only purpose is to test LibraryComponent. 没有必要将存根整合到应用程序中,它的唯一目的是测试LibraryComponent。

C) The value of this stub is only to test LibraryComponent, that is why it is a good idea to keep it as simple as possible. C)此存根的值仅用于测试LibraryComponent,这就是为什么尽可能简单地保持它的原因。

This is one way of getting the test to work without converting your components to modules. 这是在不将组件转换为模块的情况下使测试工作的一种方法。

Stubbing DropdownComponent: Stubbing DropdownComponent:

Below is a method to stub the DropdownComponent that you are attempting to use inside of LibraryComponent. 下面是一个存储您尝试在LibraryComponent中使用的DropdownComponent的方法。 Your error that you detail in your question is directly related to the fact that you have no 'app-dropdown' selector defined, yet your LibraryComponent is attempting to bind to it. 您在问题中详细说明的错误与您没有定义“app-dropdown”选择器这一事实直接相关,但您的LibraryComponent正在尝试绑定它。 Assuming you want to only test LibraryComponent within the library.component.spec.ts file, I suggest stubbing the DropdownComponent functionality rather than importing the actual component into the test. 假设您只想在library.component.spec.ts文件中测试LibraryComponent,我建议将DropdownComponent功能存根,而不是将实际组件导入测试。 I created a Stackblitz to show what I mean. 我创建了一个Stackblitz来展示我的意思。

From the Stackblitz, here is a snip from the library.component.spec.ts file: 在Stackblitz中,这是来自library.component.spec.ts文件的剪辑:

@Component({
    selector: 'app-dropdown',
    template: `<h5>Dropdown</h5>`
})
class TestDropdownComponent {
    @Input() items;
    @Input() title;
    @Output() pick = new EventEmitter<any>();
}

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

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [ RouterTestingModule ],
            declarations: [ 
                TestDropdownComponent,
                LibraryComponent
            ]
        }).compileComponents();
        fixture = TestBed.createComponent(LibraryComponent);
    }));
    it('should create the component', () => {
      const fixture = TestBed.createComponent(LibraryComponent);
      const lib = fixture.debugElement.componentInstance;
      expect(lib).toBeTruthy();
    });
});

Note - If you want to reproduce the error you detail in your question within the Stackblitz, simply comment out the line for TestDropdownComponent in the declarations of the TestBed so it looks like so: 注意 - 如果要在Stackblitz中重现您在问题中详细说明的错误,只需在TestBed的声明中注释掉TestDropdownComponent的行,这样看起来如下:

TestBed.configureTestingModule({
    imports: [ RouterTestingModule ],
    declarations: [ 
        // TestDropdownComponent,
        LibraryComponent
    ]
}).compileComponents();

And you will once again be testing just your component without a mock/stub of DropdownComponent to bind to. 而且你将再次测试你的组件而没有DropdownComponent的模拟/存根来绑定。

您需要在声明部分导入测试模块上的DropdowmComponent

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Angular Test失败无法绑定到“ header”,因为它不是“ app-previewer”的已知属性 - Angular Test failed Can't bind to 'header' since it isn't a known property of 'app-previewer' 单元测试Angular与Jasmine和Karma,错误:无法绑定到&#39;xxx&#39;,因为它不是&#39;xxxxxx&#39;的已知属性。 - Unit test Angular with Jasmine and Karma, Error:Can't bind to 'xxx' since it isn't a known property of 'xxxxxx'. Angular 单元测试错误 - 无法绑定到“formGroup”,因为它不是“表单”的已知属性 - Angular unit test error - Can't bind to 'formGroup' since it isn't a known property of 'form' 在控制台中出现错误无法绑定到“ngif”,因为它不是 angular 应用程序中“div”的已知属性 - Getting error in console Can't bind to 'ngif' since it isn't a known property of 'div' in angular app 角度-无法绑定到“属性”,因为它不是“ a”的已知属性 - Angular - Can't bind to 'property' since it isn't a known property of 'a' 无法绑定,因为它不是角度分量的已知属性 - Can't bind since it isn't a known property of angular component 无法绑定到“”,因为它不是“ angular 2”的已知属性 - can't bind to '' since it isn't a known property of '' angular 2 Angular 6 无法绑定到“*ngIf”,因为它不是已知属性 - Angular 6 Can't bind to '*ngIf' since it isn't a known property Angular 2 RC 5 - 无法绑定到&#39;...&#39;,因为它不是&#39;...&#39;的已知属性 - Angular 2 RC 5 - Can't bind to '…' since it isn't a known property of '…' Angular无法绑定到“ dirUnless”,因为它不是已知属性 - Angular Can't bind to 'dirUnless' since it isn't a known property
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM