首先,一长串类似的问题(,1,2,3,4,5 ,6,7等),但实际上没有一个答案适用于我的情况,而其他许多答案都没有得到答案。
8是following code的简单角度Minimal, Reproducible Example的一种尝试。
从项目目录运行npm run test
时
// FAILING TEST!
的测试未通过并报告Uncaught Error: ViewDestroyedError: Attempt to use a destroyed view
(much bigger project) link to travis report in the real project
app.component.ts
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit, OnDestroy {
hide: boolean = false;
someSubscription: Subscription;
constructor(private appServiceService: AppServiceService) { }
ngOnInit() {
this.someSubscription = this.appServiceService.shouldHide().subscribe(shouldHide => this.hide = shouldHide);
}
ngOnDestroy() {
this.someSubscription.unsubscribe();
}
}
app.component.html
<div class="row" id="jmb-panel" *ngIf="!hide">
Hello
</div>
app.component.spec
describe('AppComponent', () => {
let component: AppComponent;
let componentDe: DebugElement;
let fixture: ComponentFixture<AppComponent>;
const behaviorSubject = new BehaviorSubject<boolean>(false);
const appServiceStub = {
shouldHide: () => { spy.shouldHideSpyFn(); return behaviorSubject.asObservable() }
};
const spy = { shouldHideSpyFn: () => { } };
let spyShouldHide: jasmine.Spy;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [AppComponent],
schemas: [NO_ERRORS_SCHEMA],
providers: [{ provide: AppServiceService, useValue: appServiceStub }]
}).compileComponents();
}));
beforeEach(() => {
behaviorSubject.next(false);
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
componentDe = fixture.debugElement;
fixture.detectChanges();
spyShouldHide = spyOn(spy, 'shouldHideSpyFn');
});
it('should call AppServiceService#shouldHide on init', () => {
component.ngOnInit();
fixture.detectChanges();
expect(spyShouldHide).toHaveBeenCalledTimes(1);
});
it('should not render div if the AppServiceService#shouldHide observables emit true', () => {
appServiceStub.shouldHide().subscribe((li) => {
if (li) {
fixture.detectChanges();
expect(componentDe.query(By.css('#jmb-panel'))).toBeNull();
}
});
behaviorSubject.next(true);
});
// FAILING TEST!
it('should render div if the AppServiceService#shouldHide observables emit true', () => {
appServiceStub.shouldHide().subscribe((li) => {
if (!li) {
fixture.detectChanges();
expect(componentDe.query(By.css('#jmb-panel'))).not.toBeNull('Jumbotron panel should not be null');
}
});
behaviorSubject.next(false);
});
it('should create', () => {
fixture.detectChanges();
expect(component).toBeTruthy();
});
});
在发布的规范中指定测试的顺序很重要!如果更改测试顺序,则所有测试都可以通过。这是不正确的:所有测试均应独立于指定的顺序通过。实际上,在实际项目中,测试随机失败:当茉莉花建立的测试顺序是这样设置的。因此,通过更改测试顺序来“修复”该问题对我来说是行不通的。
答案 0 :(得分:2)
您为所有测试创建了一个BehaviorSubject,您在其中订阅了该主题,并且永不退订,因此在执行所有测试时它仍然有效。
Angular在每个beforeEach
上运行TestBed.resetTestingModule(),这基本上会破坏Angular应用程序并导致AppComponent视图被破坏。但是您的订阅仍然存在。
beforeEach(() => {
behaviorSubject.next(false); (3) // will run all subscriptions from previous tests
...
});
...
// FAILING TEST!
it('should render jumbotron if the user is not logged in', () => {
appServiceStub.shouldHide().subscribe((li) => { // (1)
// will be executed
1) once you've subscribed since it's BehaviorSubject
2) when you call behaviorSubject.next in the current test
3) when you call behaviorSubject.next in beforeEach block
which causes the error since AppComponent has been already destoryed
fixture.detectChanges();
....
});
behaviorSubject.next(false); // (2)
});
要解决该问题,您必须退订每个测试,或者不要对所有测试使用相同的主题:
let behaviorSubject;
...
beforeEach(async(() => {
behaviorSubject = new BehaviorSubject<boolean>(false)
TestBed.configureTestingModule({
...
}).compileComponents();
}));