【发布时间】:2017-09-15 22:52:38
【问题描述】:
我正在尝试使用 Jasmine 测试我的 Angular 组件。该组件是一个简单的表单,它向服务提交一些搜索条件,然后该服务启动并执行 Http 操作并返回一个实体数组。
我正在使用 Jasmine 来“窥探”服务方法,然后返回一个模拟实体。这个模拟实体应该保存在组件中的一个变量中。
我面临的问题是,当我断言实体已成功返回时,我在实体变量中变得未定义,这让我认为我没有正确设置我的间谍或类似的东西。
任何帮助将不胜感激!
服务:
@Injectable()
export class DynamicsSearchService {
private apiUrl = '/api/DynamicsSearch/Search';
private headers = new Headers({ 'Content-Type': 'application/json' });
constructor(private http: Http) { }
search(search: DynamicsSearch): Promise<any[]> {
search.fields = this.getDefaultFields(search.entity);
return this.http
.post(this.apiUrl, JSON.stringify(search), { headers: this.headers })
.toPromise()
.then((response) => { return this.extractResults(search.entity, response.json()); })
.catch(this.handleError);
}
...
}
组件:
@Component({
selector: 'dynamics-search-component',
templateUrl: 'dynamics-search.component.html'
})
export class DynamicsSearchComponent {
...
entities: any[];
constructor(private searchService: DynamicsSearchService) { }
submitSearch() {
this.searching = this.searched = true;
this.searchService.search(this.model)
.then(results => {
this.entities = results;
this.searching = false;
this.searchSuccessful = results !== null && results.length > 0;
});
}
...
}
测试:
describe('DynamicsSearchComponent', () => {
let fixture: ComponentFixture<DynamicsSearchComponent>;
let component: DynamicsSearchComponent;
let configuration = new Configuration();
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
FormsModule,
SharedModule
],
providers: [
BaseRequestOptions,
MockBackend,
DynamicsSearchService,
Configuration,
{
provide: Http,
useFactory: (backend: ConnectionBackend, defaultOptions: BaseRequestOptions) => {
return new Http(backend, defaultOptions);
},
deps: [
MockBackend,
BaseRequestOptions
]
}
],
declarations: [
DynamicsSearchComponent
]
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(DynamicsSearchComponent);
component = fixture.componentInstance;
});
it('on submit should get a single contact',
inject([DynamicsSearchService], (service: DynamicsSearchService) => {
var expected = [
{
contactid: 'A7806F57-002C-403F-9D3B-89778144D3E1'
}
];
const spy = spyOn(service, 'search')
.and.returnValue(Promise.resolve(expected));
component.model = new DynamicsSearch('contacts', 'A7806F57-002C-403F-9D3B-89778144D3E1', null, 'contactid');
component.submitSearch();
fixture.detectChanges();
expect(spy.calls.count()).toBe(1, `expected service search method to be called once but was called ${spy.calls.count()} times`);
expect(component.entities).toBeDefined('no entities returned');
expect(component.entities.length).toBe(1, `expected 1 entity to be returned but only ${component.entities.length} were returned`);
}
));
});
第二次期望失败,因为 component.entities 未定义。
【问题讨论】:
-
你应该把它分成两个单独的测试——这是一个非常糟糕的迹象,表明你正在拉入
Http来测试一个组件。使用MockBackend测试服务,然后使用虚假服务测试组件。此外,您可能需要fixture.detectChanges()以确保在服务数据返回后一切都得到更新。 -
谢谢乔恩。如果我能解决这个问题,我会记住这一点,并尝试重构一次服务位。在提交搜索后还添加了
fixture.detectChanges(),这似乎没有帮助。以上更新。 -
我的意思是,这将帮助您现在隔离问题并创建minimal reproducible example。最近在我的博客上为一些同事写了一些我们的服务和组件测试,可能有用:blog.jonrshar.pe/2017/Apr/16/async-angular-tests.html
-
是的,你是对的。我更想弄清楚失败的原因,以便我可以学习,但也许这是错误的方法。 Julia 的回答似乎已经解决了这个问题,所以现在我将通过将服务位分离到它自己的测试中来继续前进。
-
@jonrsharpe 我在重构我的组件测试以创建服务间谍时使用了您的博客文章作为参考,并且在我试图弄清楚如何正确模拟服务时感到沮丧之后证明它非常有用。再次感谢您。
标签: javascript angular testing jasmine