javascript - Cum să spionezi metodele unui serviciu atunci când serviciul este furnizat în rădăcina unui component


Scriu teste unitare pentru un component Angular folosind karma-jasmine. Am folosit providers în ProfileComponent-ul meu și am injectat 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();
    });
});

Am adăugat un debugger și am verificat că eșuează la autentificare. Deci, am două întrebări:

  1. Deoarece am folosit furnizori în Profile Component, pot să injectez ProfileService în fișierul profile.component.spec.ts deoarece va crea două instanțe ale aceluiași serviciu?
  2. Cum să spionezi login() și getData() din ProfileService dacă afirmația de mai jos nu funcționează...?
spyOn(profileService, 'login').and.returnValue({});

Te rog să mă ajuți cu asta.

Răspuns #1

  1. Deoarece am folosit furnizori în Componenta Profilului, pot să injectez ProfileService în fișierul profile.component.spec.ts, deoarece asta va crea două instanțe ale aceluiași serviciu..??

Răspuns: Ar trebui să creezi un Stub pentru serviciu pentru a înlocui dependența de ProfileService real folosind useClass

export class ProfileServiceStub {
    getData(id: string) {
       return of({some_obj: "value"})
    }
}

și în 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. Cum să spionezi login() și getData() din ProfileService, deoarece declarația de mai jos nu funcționează...?

Răspuns: Faceți serviciul public în constructorul componentului, astfel:

constructor(public profileService: ProfileService) { }

V-aș recomanda cu tărie să citiți articolul meu despre ceva similar, în care am testat componenta

Răspunsul #2

1.) Cred că ceea ce ai este în regulă, punerea ProfileService în array-ul provider din TestBed.configureTestingModule este bine și atunci când componenta este creată, știe cum să-l creeze. De fiecare dată când componenta este creată, va fi un furnizor separat, dar acesta va fi cazul fie că furnizezi provider-ul în componentă sau global, deoarece un test construiește și distruge (beforeEach.... TestBed.configureTestingModule....).

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

Notă: ngOnInit are nevoie de un decorator async pentru a putea folosi await.