[Angular] Testing @Input and @Output bindings
Component:
import { Component, Input, ChangeDetectionStrategy, EventEmitter, Output } from '@angular/core'; @Component({ selector: 'stock-counter', changeDetection: ChangeDetectionStrategy.OnPush, template: ` <div class="stock-counter"> <div> <div (keydown)="onKeyUp($event)" (blur)="onBlur($event)" (focus)="onFocus($event)" tabindex="0"> <p>{{ value }}</p> <div tabindex="-1"> <button type="button" tabindex="-1" (click)="increment()" [disabled]="value === max"> + </button> <button type="button" tabindex="-1" (click)="decrement()" [disabled]="value === min"> - </button> </div> </div> </div> </div> ` }) export class StockCounterComponent { @Input() step: number = 1; @Input() min: number = 0; @Input() max: number = 100; @Output() changed = new EventEmitter<number>(); value: number = 0; focused: boolean; increment() { if (this.value < this.max) { this.value = this.value + this.step; this.changed.emit(this.value); } } decrement() { if (this.value > this.min) { this.value = this.value - this.step; this.changed.emit(this.value); } } private onBlur(event: FocusEvent) { this.focused = false; event.preventDefault(); event.stopPropagation(); } private onKeyUp(event: KeyboardEvent) { let handlers = { ArrowDown: () => this.decrement(), ArrowUp: () => this.increment() }; if (handlers[event.code]) { handlers[event.code](); event.preventDefault(); event.stopPropagation(); } } private onFocus(event: FocusEvent) { this.focused = true; event.preventDefault(); event.stopPropagation(); } }
Test @Input & @Output:
import {ComponentFixture, TestBed} from '@angular/core/testing'; import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing'; import {StockCounterComponent} from './stock-counter.component'; TestBed.initTestEnvironment( BrowserDynamicTestingModule, platformBrowserDynamicTesting() ); describe('StockCounterComponent', () => { let component: StockCounterComponent; let fixture: ComponentFixture<StockCounterComponent>; beforeEach(() => { TestBed.configureTestingModule({ declarations: [ StockCounterComponent ] }); fixture = TestBed.createComponent(StockCounterComponent); component = fixture.componentInstance; component.value = 0; }); it('should not increase over the max value', () => { component.step = 20; component.max = 20; component.increment(); component.increment(); expect(component.value).toEqual(20); }); it('should not decrease below the min value', () => { component.step = 20; component.min = 0; component.value = 20; component.decrement(); expect(component.value).toEqual(0); component.decrement(); expect(component.value).toEqual(0); }); it('should call the output on a value change', () => { spyOn(component.changed, 'emit').and.callThrough(); component.step = 10; component.increment(); expect(component.changed.emit).toHaveBeenCalledWith(10) }); });
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具