Я новичок в Angular и пытаюсь научиться юнит-тестированию. У меня есть очень простой компонент, который зависит от службы ( UserService ). В хуке жизненного цикла ngOnInit () я вызываю метод службы и сохраняю данные, которые он возвращает, в свойстве. На мой взгляд, у меня есть список, связанный с этим свойством.
Это код моего компонента.
import { Component, OnInit } from "@angular/core";
import { UserService } from "../services/user.service";
import { User } from "./models/user";
@Component({
selector: "app-home",
templateUrl: "./home.component.html",
styleUrls: ["./home.component.css"]
})
export class HomeComponent implements OnInit {
users: User[] = [];
constructor(private userService: UserService) {}
ngOnInit() {
this.userService.getUsers().subscribe(x => {
this.users = x;
});
}
}
Это HTML
<ul>
<li *ngFor="let user of users" class="user-item">{{ user.name }}</li>
</ul>
Служба пользователя выглядит следующим образом
import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { User } from "../home/models/user";
@Injectable({
providedIn: "root"
})
export class UserService {
private baseUrl = "https://jsonplaceholder.typicode.com";
constructor(private http: HttpClient) {}
getUsers() {
return this.http.get<User[]>(`${this.baseUrl}/users`);
}
}
Все работает как положено. Я пытаюсь написать некоторые модульные тесты для этого компонента. Я создал несколько поддельных данных
import { User } from "../home/models/user";
export const FakeData = {
users: <User[]>[
{
id: 1,
name: "User 1",
username: "User1",
email: "User1@fakemail.com",
phone: "123 456 789",
website: "afraz.com"
},
{
id: 1,
name: "User 2",
username: "User2",
email: "User2@fakemail.com",
phone: "123 456 789",
website: "afraz.com"
}
]
};
Это тесты, которые я добавил
import {
async,
ComponentFixture,
TestBed,
fakeAsync,
flush
} from "@angular/core/testing";
import { HttpClientModule } from "@angular/common/http";
import { HomeComponent } from "./home.component";
import { UserService } from "../services/user.service";
import { of } from "rxjs";
import { FakeData } from "../fakes/fake-data";
import { By } from "@angular/platform-browser";
describe("HomeComponent", () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
let userService: UserService;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [HttpClientModule],
declarations: [HomeComponent],
providers: [UserService]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
// Mock the user service
userService = TestBed.get(UserService);
spyOn(userService, "getUsers").and.returnValue(of(FakeData.users));
});
afterEach(() => {
fixture.destroy();
component = null;
});
it("should create component", () => {
expect(component).toBeTruthy();
});
it("should get 'Users' from the service", fakeAsync(() => {
fixture.whenStable().then(() => {
expect(component.users.length).toEqual(2);
});
}));
it("should bind 'Users' to the view", fakeAsync(() => {
fixture.whenStable().then(() => {
const userList = fixture.debugElement.queryAll(By.css(".user-item"));
expect(userList.length).toEqual(2);
});
}));
});
Теперь вот где я запутался, я получаю следующую ошибку в выводе
'Spec' HomeComponent должен связывать 'Users' с представлением 'не имеет ожиданий.'
'Spec' HomeComponent должен получать 'Users' из сервиса 'не имеет никаких ожиданий. '
Проблема в том, что если я использую Async вместо fakeAsync , то вызывается фактическая служба пользователя, а я не хочусделать это. Я ожидаю, что, поскольку у меня есть следующая строка кода
spyOn(userService, "getUsers").and.returnValue(of(FakeData.users));
в блоке beforeEach () , следовательно, фактический код не должен выполняться, а вместо этогоэти жестко закодированные данные должны быть возвращены. Кто-нибудь может помочь и объяснить, что мне не хватает?
ОБНОВЛЕНИЕ: Вот мой последний рабочий код тестов. Спасибо за быструю помощь! Надеюсь, это поможет кому-то еще:)
import { async, ComponentFixture, TestBed } from "@angular/core/testing";
import { HomeComponent } from "./home.component";
import { UserService } from "../services/user.service";
import { of } from "rxjs";
import { FakeData } from "../fakes/fake-data";
import { By } from "@angular/platform-browser";
describe("HomeComponent", () => {
let component: HomeComponent;
let fixture: ComponentFixture<HomeComponent>;
let userServiceSpy: jasmine.SpyObj<UserService>;
beforeEach(async(() => {
userServiceSpy = jasmine.createSpyObj(["getUsers"]);
TestBed.configureTestingModule({
imports: [],
declarations: [HomeComponent],
providers: [{ provide: UserService, useValue: userServiceSpy }]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(HomeComponent);
component = fixture.componentInstance;
userServiceSpy.getUsers.and.returnValue(of(FakeData.users));
fixture.detectChanges();
});
afterEach(() => {
fixture.destroy();
component = null;
});
it("should create component", () => {
expect(component).toBeTruthy();
});
it("should have 'Users' populated", () => {
expect(component.users.length).toEqual(2);
});
it("should bind 'Users' to the view", () => {
const userList = fixture.debugElement.queryAll(By.css(".user-item"));
expect(userList.length).toEqual(2);
});
});