Angular2 Karma test says “TypeError: this._subscri

2019-07-03 19:28发布

问题:

I have an Angular 2 application that works OK so far. My GlobalsService.getGlobals() fetches "USA States" through my OptionService.getOptions(). Where my code falls down is in Karma / Jasmine testing.

When I call getGlobals() in my current test setup I get the "this._subscribe is not a function" error. I'm finding it hard to debug this because Google Chrome isn't cooperating.

My getGlobals() code with debugging added. I split the code between getOptions() and .subscribe() to see what is going on.

public getGlobals(): void {
  let asdf = this.optionService.getOptions();

  console.log("asdf is " + (typeof asdf) + ", asdf.subscribe is: " + asdf.subscribe);
  try {
    asdf.subscribe(
      (options: Option[]) => this.fillOptions(options),
      (error: string) => this.error = error
    );
  } catch(error) {
    console.log("caught error involving asdf, it is: " + error);
    throw error;
  }
}

When I run this code I get:

 LOG: 'asdf is object, asdf.subscribe is: function (observerOrNext, error, complete) {
[1]         var operator = this.operator;
[1]         var sink = toSubscriber_1.toSubscriber(observerOrNext, error, complete);
[1]         if (operator) {
[1]             operator.call(sink, this.source);
[1]         }
[1]         else {
[1]             sink.add(this._subscribe(sink));
[1]         }
[1]         if (sink.syncErrorThrowable) {
[1]             sink.syncErrorThrowable = false;
[1]             if (sink.syncErrorThrown) {
[1]                 throw sink.syncErrorValue;
[1]             }
[1]         }
[1]         return sink;
[1]     }'

The code of asdf.subscribe is what you see from node_modules/rxjs/Observable.js. So the return from getOptions() is a Observable. Among its many properties it has these features:

_subscribe: Array[4] (each item is an Object of my Option data type)
_proto_: Object
  _subscribe: (subscriber) 
  subscribe: (observer or next, error, complete)

But if I let the code continue I get the error. In the place with the asdf variable "this" is GlobalsService.

My mocked OptionService code is:

@Injectable()
export class MockOptionService {
  constructor() { }

  getOptions(): Observable<Option[]> {
    let options: Option[] = [
      { id: 'IN', name: 'Indiana', topic: Option.TOPIC_STATE },
      { id: 'NJ', name: 'New Jersey', topic: Option.TOPIC_STATE },
      { id: 'CONCERT', name: "Concert", topic: Option.TOPIC_EVENT_TYPE },
      { id: 'NY', name: 'New York', topic: Option.TOPIC_STATE }
    ];
    return Observable.create(options);
  }

}

This is injected through:

describe('Globals Service', () => {

  beforeEach(() => {

    TestBed.configureTestingModule({
      providers: [
        { provide: OptionService, useClass: MockOptionService },
        { provide: GlobalsService, useClass: GlobalsService }
      ]
    });
[SNIP]

});

As I said, I'm having trouble with Chrome when debugging this. If I show the Observable.js source in the debugger and try to set a breakpoint the browser displays a blank page for the non-existent Observable.ts file. The breakpoint doesn't show in either place. The code is stopping on the non-existent Observable.ts:92.

My questions are, then:

1. Can I convince Chrome to give me a Javascript breakpoint in a Typescript world?

2. What is the proper function for creating an object array and then attaching it to an Observable? I suspect that Observable.create() is wrong.

Thanks, Jerome.

回答1:

Use Observable.of instead. Observable.create expects a subscribe function. Observable.create is meant to be used to create a "custom-logic" Observable, while Observable.of just creates a basic Observable that emits the value that you pass to it.