【问题标题】:Mocking service calls that return observables in Angular 2?在 Angular 2 中模拟返回 observables 的服务调用?
【发布时间】:2017-06-16 18:05:48
【问题描述】:

我一直在尝试模拟一些在我的 Angular 应用程序中返回 observable 的服务调用,但我根本无法在我的代码中创建一个有效的 observable 来触发像 catch()map() 这样的调用。例如:

我的服务:

create(): Observable<any> {
    return this.http
    .post('/api/stuff', { id: 123 })
    .catch(this.handleError)
    .map(this.extractData);
}

我的规格:

let authHttpMock = mock(AuthHttp);
when(authHttpMock.post('/api/stuff', { id: 123 })).thenReturn(Observable.create(observer => {
  observer.error(new Error('500!'));
}));

const myService = new MyService(instance(authHttpMock));
myService.create({ id: 123 }).subscribe(
    result => {
    expect(result).toBeTruthy();
    }
);

覆盖率分析告诉我handleError 方法从未执行过。在成功的 observable 的情况下,它也不会通过 extractData 方法。

那个 observable 要去哪里?我怎样才能返回一个适当的 observable 来测试这样的调用?

【问题讨论】:

  • 是否订阅了 observable?在有订阅之前,observable 不会执行。
  • 没有。我应该订阅测试本身吗?
  • 我在我的规范中添加了对 create 调用的订阅调用,但没有任何改变。
  • 我什至在其上添加了一个 expect(result).toBeTruthy() ,但什么也没有。然后我尝试了一个expect(result).toBeUndefined()。什么都没有。
  • Vinny,您能说说您是如何运行测试的以及您使用什么模拟库吗?从语法来看,它看起来像 jasmine-mocks,但我未能在基于浏览器的 Karma 测试中运行它。

标签: angular


【解决方案1】:

我相信在你的测试代码的某个地方你需要有这个代码:

AuthHttp.post('/api/stuff', {id : 123 }).subscribe(data => {});

【讨论】:

    【解决方案2】:

    有两种方法可以实现。

    首先,在您的服务方法中,使用以下内容:

    create(id:number):Obserable<any> {
      let url:string = 'http://localhost:3000/api/stuff';
      let data:any = {
        id: id
      };
    
      return this.http.post(url, JSON.stringify(data))
        .map((res:Response) => res.json());
        .catch((error:any) => this.handleError(error))
    }
    

    第二种方法是,调用使用服务如下:

    create(id:number):Obserable<any> {
      let url:string = 'http://localhost:3000/api/stuff';
      let data:any = {
        id: id
      };
    
      return this.http.post(url, JSON.stringify(data))
        .map((res:Response) => res.json());
      }
    

    当你调用服务方法时,使用这个:

    this._service.create(123)
      .subscribe((res:any) => {
        //do whatever with success
      },
      (error:any) => {
        this.handleError(error)
      });
    

    您用来提取数据的方法this.extractData(),可以在服务内部调用而不是res.json(),但总体结果是一样的。

    希望这会有所帮助:)

    【讨论】:

    • 你测试你的代码了吗?因为它给出了与我的尝试相同的错误。代码永远不会被测试套件执行。
    【解决方案3】:

    您需要先创建 mockData 以便服务可以使用它。下面有 2 个文件。将您的组件和服务名称分别放在 COMPONENT_NAME 和 SERVICE_NAME 的位置。

    规格文件 ts

    describe('WalletComponent', () => {
      let component: WalletManagementComponent;
      let fixture: ComponentFixture<WalletManagementComponent>;
    
      beforeEach(async(() => {
        TestBed.configureTestingModule({
          imports: [ReactiveFormsModule],
          declarations: [COMPONENT_NAME],
          schemas: [NO_ERRORS_SCHEMA],
          providers: [
            {provide: APP_BASE_HREF, useValue: '/'},
            {provide: SERVICE_NAME, useClass: serviceMockData}      
          ]
        }).compileComponents();
      }));
    
      beforeEach(() => {
        fixture = TestBed.createComponent(COMPONENT_NAME);
        component = fixture.componentInstance;
        fixture.detectChanges();
      });
    
      it('Should create WalletComponent', () => {
        expect(component).toBeTruthy();
      });
    
     });
    

    您需要在根目录中创建名为 testHelper(或任何名称)的目录

    serviceMockData ts

    import {Observable} from 'rxjs/Observable';
    
    const result = {
      data: [
        {id: 0, name: 'Temp Data 1'},
        {id: 1, name: 'Temp Data 2'},
      ]
    }; 
    
    export class serviceMockData {
      constructor() {
      }
    
      getWalletAudits() {
        return Observable.of(result).map(res => res);
      }
    
    }
    

    【讨论】:

      【解决方案4】:

      如果您订阅错误处理程序怎么办?例如。在您的规范中:

      const myService = new MyService(instance(authHttpMock));
      myService.create({ id: 123 }).subscribe(
        result => {
          throw new Error('Not supposed to be here.')
        },
        err => expect(err).toBeDefined(),
      );
      

      (虽然我认为不应该这样)。

      这就是我模拟失败的后端调用的方式:

      // first, I provide mock http in TestBed.configure...
      ...
      providers:
        {
          provide: Http,
          useFactory: (backend: ConnectionBackend, options: BaseRequestOptions) => new Http(backend, options),
          deps: [MockBackend, BaseRequestOptions]
        },
        ... // other providers, like your mockAuthHttp
      ]
      
      
      // than in the test, I tell the mockBackend to fail.
      // including other stuff here to, just to show a few more things that can be done
      
      it('should handle errors if backend doesn\'t like us', async(() => {
        let service = TestBed.get(MyService);
        let mockBackend = TestBed.get(MockBackend);
        mockBackend.connections.subscribe((c: any) => {
          if (c.request.url.indexOf('/api/stuff') !== -1) {
            // you can check stuff here, like c.request.method or c.request._body
            const err: ResponseError = new ResponseError('Stuff not good.');
            err.status = 404;
            return c.mockError(err);
          }
          throw new Error('Wrong url called.');
        });
      
        myService.create('whatever').subscribe((r: any) => {
          throw new Error('Should not have been a success.');
        },
        (err: any) => {
          expect(err.message).toEqual('Stuff not good.');
        });
      }));
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-06-17
        • 2017-02-15
        • 2018-12-18
        • 1970-01-01
        • 2015-05-29
        • 2018-03-24
        • 1970-01-01
        • 2018-10-06
        相关资源
        最近更新 更多