【问题标题】:How can a mocked method be called and not be detected by spy?如何调用模拟方法而不被间谍检测到?
【发布时间】:2020-08-10 08:06:30
【问题描述】:

这是我的组件:

@Component({
    selector: 'app-signup',
    templateUrl: './signup.component.html',
    styleUrls: ['./signup.component.scss']
})
export class SignUpComponent implements OnInit {
    specialLink: string;

    constructor(
        private activatedRoute: ActivatedRoute,
    ) {
        this.specialLink = this.activatedRoute.snapshot.params.id;
        console.log('TEST1', this.specialLink);

        if (this.specialLink !== undefined) {
            this.setSpecialSignup();
        }
    }

    setSpecialSignup() {
        console.log("CALLED;");
    }

这是我的测试:

describe('SignUpComponent', () => {
  let component: SignUpComponent;
  let fixture: ComponentFixture<SignUpComponent>;
  let ActivatedRouteMock: any;
  
  beforeEach(async(() => {
    ActivatedRouteMock = {
      snapshot: {
        params: { id: 123 }
      },
    };

    TestBed.configureTestingModule({
      declarations: [ SignUpComponent ],
      imports: [ RouterTestingModule ],
      providers: [
        {provide: ActivatedRoute, useValue: ActivatedRouteMock}
      ]
    })
    .compileComponents();
  }));

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


  describe('Patient Side', () => {
    it('should call setSpecialSignup() when user is coming from specialLink', () => {
      spyOn(component, 'setSpecialSignup');
      fixture.detectChanges();
      expect(component.setSpecialSignup).toHaveBeenCalled();
    });

我正在测试是否调用了 setSpecialSignup。它是! 2 'console.log' 证明了这一点。那么,为什么会出现这个错误:Expected spy setSpecialSignup to have been called.? 我错过了什么?

看来我需要能够更改 ActivatedRouteMock.snapshot.params.id 的值。有时它必须是未定义的,有时它必须是“某物”。我怎样才能做到这一点 ?我尝试了ActivatedRouteMock.snapshot.params.id = 'something',但它根本没有改变值,即使使用fixture.detectChanges()。我该怎么做?

【问题讨论】:

  • 可能你的间谍太晚了。但是不要窥探你真正想要测试的东西——测试替身是为合作者准备的。
  • 感谢您的回答。那我应该监视什么?
  • 这取决于setSpecialSignup 实际上做了什么。
  • 设置一些变量并从数据库(Firebase)获取数据
  • 那么测试替身应该是它访问 Firebase 所通过的注入服务。

标签: angular unit-testing mocking karma-jasmine spy


【解决方案1】:

我们看到setSpecialSignupconstructor 内部被调用。 constructor 在此行 fixture = TestBed.createComponent(SignUpComponent); 上被调用。你创建间谍太晚了。如果您希望此功能更易于测试,请将组件的逻辑移动到 ngOnInit 生命周期挂钩。它将在第一个 fixture.detectChanges() 上调用,您的测试应该没问题

constructor(
        private activatedRoute: ActivatedRoute,
    ) { }
   ngOnInit() {
        this.specialLink = this.activatedRoute.snapshot.params.id;
        console.log('TEST1', this.specialLink);

        if (this.specialLink !== undefined) {
            this.setSpecialSignup();
        }
    }

【讨论】:

  • 完美运行。感谢您的解释 !因为我需要测试 this.specialLink = this.activatedRoute.snapshot.params.id;你知道如何在我的测试中更改 ActivatedRouteMock 中的“id”值吗?
  • 在这里您完全重新定义了您的路由模拟`ActivatedRouteMock = ...`,并将其定义为123 params: { id: 123 }。在第一次detectChanges()之前将其更改为您喜欢的任何内容。
  • 我在第一个 detectChanges() 之前添加了ActivatedRouteMock = { snapshot: { params: { id: 123 } }, };,但我不起作用。
  • 我的意思是值改变了。但是由于该值不是未定义的,所以应该调用 setSpecialSignup 并且我再次收到错误:Expected spy setSpecialSignup to have been called.
  • 你可以在你想用空值测试它的测试中做ActivatedRouteMock.snapshot.params.id = null;
【解决方案2】:

你能把你在构造函数中的代码移动到ngOnInit()

@Component({
    selector: 'app-signup',
    templateUrl: './signup.component.html',
    styleUrls: ['./signup.component.scss']
})
export class SignUpComponent implements OnInit {
    specialLink: string;

    constructor(
        private activatedRoute: ActivatedRoute,
    ) {
       
    }
ngOnInit(){
 this.specialLink = this.activatedRoute.snapshot.params.id;
        console.log('TEST1', this.specialLink);

        if (this.specialLink !== undefined) {
            this.setSpecialSignup();
        }
}

    setSpecialSignup() {
        console.log("CALLED;");
    }

你能试试下面的代码吗?

describe('SignUpComponent', () => {
  let component: SignUpComponent;
  let fixture: ComponentFixture<SignUpComponent>;
  let ActivatedRouteMock: any;
  
  beforeEach(async(() => {
    ActivatedRouteMock = {
      snapshot: {
        params: { id: 123 }
      },
    };

    TestBed.configureTestingModule({
      declarations: [ SignUpComponent ],
      imports: [ RouterTestingModule ],
      providers: [
        {provide: ActivatedRoute, useValue: ActivatedRouteMock}
      ]
    })
  .compileComponents().then(() => {
          fixture = TestBed.createComponent(PriceModelListComponent);
          component = fixture.componentInstance;         
        });
  }));

 
  describe('Patient Side', () => {
    it('should call setSpecialSignup() when user is coming from specialLink', () => {
      spyOn(component, 'setSpecialSignup');
      fixture.detectChanges();
      expect(component.setSpecialSignup).toHaveBeenCalled();
    });

用于模拟参数

在规范中的提供者内部

providers: [ { provide: ActivatedRoute, useValue: { // Mock queryParams: of( { id_params: 'id_params_test' } ), params: of( { id_query_params: 'id_query_params_test' } ) } } ],

【讨论】:

  • 感谢您的回答,但不幸的是它不起作用。没有变化。
  • .compileComponents().then(() => { fixture = TestBed.createComponent(PriceModelListComponent); component = fixture.componentInstance; });
  • 你能把这个函数从构造函数移动到 ngOnInit() 吗?
  • 现在可以使用了,谢谢。由于我需要测试this.specialLink = this.activatedRoute.snapshot.params.id;,您知道如何在我的测试中更改 ActivatedRouteMock 中的“id”值吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-22
  • 1970-01-01
  • 1970-01-01
  • 2019-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多