简体   繁体   English

如何测试 angular 组件

[英]How to test angular component

I have a component, which depend on a service, which has some dependencies too.我有一个组件,它依赖于一个服务,它也有一些依赖关系。 (Component call service method, and get an observable back using subject.) (组件调用服务方法,并使用主题获取可观察的返回。)

Component:零件:

export class CoursesComponent implements OnInit {
  courses: Observable<Course[]>;

  constructor(private courseService: CourseService) {
    this.courseService.loadCourses();
  }

  ngOnInit(): void {
    this.courses = this.courseService.course;
  }

}

Service服务

@Injectable()
export class CourseService {
  private courseSubject: Subject<Course[]> = new Subject();
  course = this.courseSubject.asObservable();

  constructor(private courseApiService: CourseApiService,
              private router: Router,
              private route: ActivatedRoute) { }

  loadCourses(title?: string, end?: number): Subscription {
    return this.getCourses(title, end).subscribe((courses: Course[]) => this.courseSubject.next(courses));
  }

  private getCourses(title?: string, end?: number): Observable<Course[]> {
    return this.courseApiService.getCourses(title, end);
  }
}

I try to test component and have trouble: I don't want inject real service to TestBed, so at first I tried to spy on all courseService methods:我尝试测试组件并遇到麻烦:我不想向 TestBed 注入真正的服务,所以起初我试图监视所有 courseService 方法:

const courseServiceMethods = [
  'loadCourses',
  'postCourse',
  'editCourse',
  'getCourseById',
  'removeCourseById',
  'navigateById'];

describe('CoursesComponent', () => {
  let component: CoursesComponent;
  let fixture: ComponentFixture<CoursesComponent>;
  let courseService: SpyObj<CourseService>;

  beforeEach(async(() => {
    courseService = jasmine.createSpyObj('CourseService', courseServiceMethods);
    TestBed.configureTestingModule({
      declarations: [ CoursesComponent, SearchComponent ],
      providers: [
        {provide: CourseService, useValue: courseService},
      ]
    })
    .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(CoursesComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  describe('onDelete()', () => {
    it('should call removeCourseById()',  () => {
      component.onDelete('2');

      expect(courseService.removeCourseById).toHaveBeenCalled();
    });
  });

but with this method I can only check if some of the methods from service are called and can`t get observable which gives me my data.但是使用这种方法,我只能检查是否调用了服务中的某些方法并且无法观察到这给了我的数据。 So I have another option: in spec.ts write my own mock class for course.service and provide it in TestBed:所以我有另一个选择:在 spec.ts 中为 course.service 编写我自己的模拟 class 并在 TestBed 中提供:

@Injectable()
class Mock {
  private courseSubject: Subject<Course[]> = new Subject();
  course = this.courseSubject.asObservable();
  loadCourses(title?: string, end?: number): Subscription {
    return this.getCourses(title, end).subscribe((courses: Course[]) => this.courseSubject.next(courses));
  }

  private getCourses(title?: string, end?: number): Observable<Course[]> {
    return of(coursesMockArray);
  }
}

but with this option I have to mock all methods from courseService, and if it is too big?但是有了这个选项,我必须模拟 courseService 中的所有方法,如果它太大了? Is there any chance to spy on methods from course service and check returning data?是否有机会监视课程服务中的方法并检查返回数据?

You can do:你可以做:

it('should call removeCourseById()',  () => {
      spyOn(courseService.removeCourseById).and.returnValue(of(true)); // or whatever you want to mock
      component.onDelete('2');

      expect(courseService.removeCourseById).toHaveBeenCalled();
    });

I would do the subscribing in the components and not in the services although they are finite observables, it seems weird to me that they are being subscribed in the service.我会在组件中而不是在服务中进行订阅,尽管它们是有限的可观察对象,但对我来说,它们在服务中被订阅似乎很奇怪。

Solution 1解决方案 1

You need to create fixture of the service (probably you have if by default) if not you can create like:您需要创建服务的固定装置(如果默认情况下您可能有),如果没有,您可以创建如下:

export class CourseServiceFixture implements Readonly<CourseService>{
 public loadCourses= jest.fn(); 
//i am using jest you might be using jasmine you may have to write jasmine mock fun
}

then in spec.ts file of component create a variable:然后在组件的 spec.ts 文件中创建一个变量:

let mockCourseService: CourseServiceFixture;

now you need to assign the variable as object and use it in the providers[] array:现在您需要将变量分配为 object 并在 providers[] 数组中使用它:

beforeEach(async(() => {
//this is must
mockCourseService = new CourseServiceFixture();
 courseService = jasmine.createSpyObj('CourseService', courseServiceMethods);
    TestBed.configureTestingModule({
      declarations: [ CoursesComponent, SearchComponent ],
      providers: [
        {provide: CourseService, useValue: mockCourseService},
      ]
    })
    .compileComponents();
  }));

now each time you want to use the service you can make it mock return value of the method defined in the fixture file eg:现在,每次您想使用该服务时,您都可以使其模拟夹具文件中定义的方法的返回值,例如:

it('should have some courses',  () => {
 mockCourseService.loadCourses.mockReturnValue(['a','b','c']);
fixture.detectChanges();

expect(component.courses).toBe([...]);
});

Solution 2 incase you don't want to create fixture file it is also given in the docs解决方案 2如果您不想创建夹具文件,它也在文档中给出

let mockCourseService: Partial<CourseService>;

beforeEach(() => {
 mockCourseService= {
    courses: [...],
  };
  providers: [ { provide: CourseService, useValue: mockCourseService} ],
}

you also need to inject the service:您还需要注入服务:

userService = TestBed.inject(UserService);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM