当在组件的根目录中提供服务时如何监视服务方法

How to spy on methods of service when the service is provided in the root of Component

我正在使用 karma-jasmine 为 angular 组件编写单元测试。我在我的 ProfileComponent 中使用了提供程序并注入了 ProfileService。

profile.component.ts

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

    public data;

    constructor(private profileService: ProfileService) {}

    ngOnInit() {
        await this.profileService.login();
        this.profileService.getData('A').subscribe((result) => {
            this.data = result;
        });
    }

}

profile.service.ts

@Injectable()
export class ProfileService {

    private baseUrl: string;

    constructor(private http: HttpClient) {
        this.baseUrl = 'https://localhost:4002';
    }

    public async login() {
        const loginUrl = `${this.baseUrl}/api/Login`;
        return this.http.post(loginUrl, {}, { headers }).toPromise();
    }

    getData(id: string) {
        const url = `${this.baseUrl}/api/GetData/${id}`;
        return this.http.get(url, { headers });
    }
}

profile.component.spec.ts

const data = ['A', 'B', 'C'];

describe('Client.App.ProfileComponent', () => {
    let component: ProfileComponent;
    let fixture: ComponentFixture < ProfileComponent > ;
    let profileService: ProfileService;
    let profileData;

    beforeEach(async (() => {
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule],
            declarations: [ProfileComponent],
            providers: [ProfileService],
            schemas: [NO_ERRORS_SCHEMA]
        }).compileComponents();
    }));

    beforeEach(fakeAsync(() => {
        profileData = new Subject();
        profileService = TestBed.inject < ProfileService > (ProfileService);
        spyOn(profileService, 'login').and.returnValue({});
        spyOn(profileService, 'getData').and.returnValue(profileData);
        fixture = TestBed.createComponent(ProfileComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    }));

    it('shall create the component', () => {
        profileData.next(data);
        fixture.detectChanges();
        expect(component).toBeDefined();
    });
});

我添加了一个调试器并检查它是否登录失败。所以,我有两个问题:

  1. 因为我在 Profile Component 中使用了提供程序,我可以在 profile.component.spec.ts 文件中注入 ProfileService 吗,因为它会创建同一服务的两个实例?
  2. 如何监视 ProfileService 的 login() 和 getData() 如下语句不起作用...?
spyOn(profileService, 'login').and.returnValue({});

请帮我解决这个问题。

1.) 我认为你有什么很好,将 ProfileService 放在 TestBed.configureTestingModuleprovider 数组中是好的,当组件被创建时,它知道如何制作它。每次创建组件时,它都将是一个单独的提供者,但无论是在组件中还是在全局范围内提供提供者,都会出现这种情况,因为测试会构造和析构 (beforeEach.... TestBed.configureTestingModule...) .

2.) 试试`spyOn(profileService, 'login').and.returnValue(Promise.resolve({}));

旁注:ngOnInit 需要异步装饰器才能使用 await。

首先你需要提供一个模拟而不是提供实际的服务实例。之后,您需要提供该服务方法的伪造实现。请在下面找到所需的更改和内联评论。

const data =['A','B','C'];

describe('Client.App.ProfileComponent', () => {
    let component: ProfileComponent;
    let fixture: ComponentFixture<ProfileComponent>;
    let profileService: jasmine.SpyObj<ProfileService>;

    beforeEach(async(() => {
       // create a spied service and use it inside providers array
        profileService = jasmine.createSpyObj('ProfileService', ['login', 'getData']);
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule],
            declarations: [ProfileComponent],
            providers: [ { provide: ProfileService, useValue: profileService ],
            schemas: [NO_ERRORS_SCHEMA]
        }).compileComponents();
       // provide fake implementation to service methods
        profileService.login.and.returnValue(Promise.resolve('success'));
        profileService.getData.and.returnValue(of(data)); // of is rxjs operators- import
        fixture = TestBed.createComponent(ProfileComponent);
        component = fixture.componentInstance;
        fixture.detectChanges();
    }));

    it('shall create the component', () => {
        expect(component).toBeDefined();
    });
});
  1. 因为我在 Profile 组件中使用了提供程序,我可以在 profile.component.spec.ts 文件中注入 Profileervice,因为它将创建同一服务的两个实例..??

答案:您应该为该服务创建一个 Stub,以使用 useClass

替换对实际 ProfileService 的依赖
export class ProfileServiceStub {
    getData(id: string) {
       return of({some_obj: "value"})
    }
}

在spec.ts

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            imports: [HttpClientTestingModule, ReactiveFormsModule, RouterTestingModule],
            declarations: [ProfileComponent],
            providers: [ {provide: ProfileService , useClass: ProfileServiceStub } ], // <-- useclass here
            schemas: [NO_ERRORS_SCHEMA]
        }).compileComponents();
  1. 如何监视 ProfileService 的 login() 和 getData() 如下语句不起作用...?

答:在组件构造函数中创建服务public如下:

constructor(public profileService: ProfileService) { }

强烈推荐你阅读my article on something similar where I have tested the component