使用@Input()进行Angular2单元测试

har*_*pdx 53 unit-testing jasmine typescript angular angular-test

我有一个@Input()在实例变量上使用注释的组件,我正在尝试为该openProductPage()方法编写单元测试,但是我在设置单元测试方面有点迷失.我可以将该实例变量公之于众,但我认为我不应该诉诸于此.

如何设置我的Jasmine测试以便注入模拟产品(提供?)并且我可以测试该openProductPage()方法?

我的组件:

import {Component, Input} from "angular2/core";
import {Router} from "angular2/router";

import {Product} from "../models/Product";

@Component({
    selector: "product-thumbnail",
    templateUrl: "app/components/product-thumbnail/product-thumbnail.html"
})

export class ProductThumbnail {
    @Input() private product: Product;


    constructor(private router: Router) {
    }

    public openProductPage() {
        let id: string = this.product.id;
        this.router.navigate([“ProductPage”, {id: id}]);
    }
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*lis 41

如果您使用TestBed.configureTestingModule编译测试组件,这是另一种方法.它与接受的答案基本相同,但可能更类似于angular-cli生成规范的方式.FWIW.

import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { DebugElement } from '@angular/core';

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ 
        TestComponentWrapper,
        ProductThumbnail
      ],
      schemas: [CUSTOM_ELEMENTS_SCHEMA]
    })
    .compileComponents();

    fixture = TestBed.createComponent(TestComponentWrapper);
    component = fixture.debugElement.children[0].componentInstance;
    fixture.detectChanges();
  });

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

@Component({
  selector: 'test-component-wrapper',
  template: '<product-thumbnail [product]="product"></product-thumbnail>'
})
class TestComponentWrapper {
  product = new Product()
}
Run Code Online (Sandbox Code Playgroud)


Vaz*_*yan 33

这是来自官方文档https://angular.io/docs/ts/latest/guide/testing.html#!#component-fixture.因此,您可以创建新的输入对象expectedHero并将其传递给组件comp.hero = expectedHero

还要确保调用fixture.detectChanges();last,否则属性不会绑定到组件.

工作实例

// async beforeEach
beforeEach( async(() => {
    TestBed.configureTestingModule({
        declarations: [ DashboardHeroComponent ],
    })
    .compileComponents(); // compile template and css
}));

// synchronous beforeEach
beforeEach(() => {
    fixture = TestBed.createComponent(DashboardHeroComponent);
    comp    = fixture.componentInstance;
    heroEl  = fixture.debugElement.query(By.css('.hero')); // find hero element

    // pretend that it was wired to something that supplied a hero
    expectedHero = new Hero(42, 'Test Name');
    comp.hero = expectedHero;
    fixture.detectChanges(); // trigger initial data binding
});
Run Code Online (Sandbox Code Playgroud)

  • 使用的英雄元素在哪里 (5认同)
  • 这应该是公认的答案.没有黑客,纯粹的嘲弄.谢谢 (2认同)
  • 对于编写需要测试多个特定情况的测试来说,在每个之前使用来设置每个测试都需要动态的数据似乎是一种非常糟糕的模式 (2认同)

Thi*_*ier 19

product在测试中加载组件实例后,需要在组件实例上设置该值.

这里的示例是输入中的一个简单组件,您可以将其用作用例的基础:

@Component({
  selector: 'dropdown',
  directives: [NgClass],
  template: `
    <div [ngClass]="{open: open}">
    </div>
  `,
})
export class DropdownComponent {
  @Input('open') open: boolean = false;

  ngOnChanges() {
    console.log(this.open);
  }
}
Run Code Online (Sandbox Code Playgroud)

并进行相应的测试:

it('should open', injectAsync([TestComponentBuilder], (tcb: TestComponentBuilder) => {
  return tcb.createAsync(DropdownComponent)
  .then(fixture => {
    let el = fixture.nativeElement;
    let comp: DropdownComponent = fixture.componentInstance;

    expect(el.className).toEqual('');

    // Update the input
    comp.open = true; // <-----------

    // Apply
    fixture.detectChanges(); // <-----------

    var div = fixture.nativeElement.querySelector('div');
    // Test elements that depend on the input
    expect(div.className).toEqual('open');
  });
}));
Run Code Online (Sandbox Code Playgroud)

请参阅此plunkr作为示例:https://plnkr.co/edit/YAVD4s?p = preview .

  • 在 OP 的示例中,设置的 `@Input` 属性是私有的。除非我弄错了,否则这种方法在这种情况下不会起作用,因为 tsc 将禁止对私有字段的引用。 (3认同)
  • 感谢您指出了这一点!我错过了这个领域是私人的。我再次考虑了你的评论和“私人”方面。我想知道在这个字段上使用“private”关键字是否是一件好事,因为它实际上并不是“私有”...我的意思是它将由 Angular2 从类外部进行更新。有兴趣听取您的意见;-) (2认同)
  • 你问了一个有趣的问题,但我认为你要问的真正问题是,在打字稿中使用"私人"是否是一件好事,因为它不是"实际上是私人的" - 即,因为它不能强制执行运行时,仅在编译时.我个人喜欢它,但也理解反对它的论点.然而,在一天结束时,微软选择在TS中使用它,而Angular选择TS作为主要语言,我认为我们不能断然说使用主要语言的主要功能是一个坏主意. (2认同)
  • 非常感谢您的回答!我个人坚信使用TypeScript是一件好事。它实际上有助于提高应用程序质量!我不认为使用`private`并不是坏事,即使它在运行时不是真的私有:-)那对于这种特殊情况,我不确定使用`private`是不是一件好事由Angular2在课外管理... (2认同)
  • 我正在尝试将其与新的TestBed.createComponent一起使用,但是当我调用Fixture.detectChanges()时,它不会触发ngOnChanges调用。您知道如何使用“新系统”对其进行测试吗? (2认同)

dre*_*ore 15

我经常这样做:

describe('ProductThumbnail', ()=> {
  it('should work',
    injectAsync([ TestComponentBuilder ], (tcb: TestComponentBuilder) => {
      return tcb.createAsync(TestCmpWrapper).then(rootCmp => {
        let cmpInstance: ProductThumbnail =  
               <ProductThumbnail>rootCmp.debugElement.children[ 0 ].componentInstance;

        expect(cmpInstance.openProductPage()).toBe(/* whatever */)
      });
  }));
}

@Component({
 selector  : 'test-cmp',
 template  : '<product-thumbnail [product]="mockProduct"></product-thumbnail>',
 directives: [ ProductThumbnail ]
})
class TestCmpWrapper { 
    mockProduct = new Product(); //mock your input 
}
Run Code Online (Sandbox Code Playgroud)

请注意,使用这种方法product,ProductThumbnail类中的任何其他字段都可以是私有的(这是我比Thierry的方法更喜欢它的主要原因,尽管事实上它更加冗长).