3

I have this Rxjs testing code. It fail deliberately, because i want to show you the failing log. Which i found hard to understand, or at least i cannot read it fluently.

Someone can explain me what means : $[i].frame = i' to equals i'' ?

import { delay } from 'rxjs/operators';
import { TestScheduler } from 'rxjs/testing';

describe('Rxjs Testing', () => {

  let s: TestScheduler;

  beforeEach(() => {
    s = new TestScheduler((actual, expected) => {
      expect(actual).toEqual(expected);
    });
  });

  it('should not work', () => {
    s.run(m => {
      const source = s.createColdObservable('-x-y-z|');
      const expected = '-x-y-z|'; // correct expected value is '---x-y-z|'

      const destination = source.pipe(delay(2));
      m.expectObservable(destination).toBe(expected);
    });
  });
});

enter image description here

bubbles
  • 2,597
  • 1
  • 15
  • 40
  • 1
    `frame` is an internal representation of time in `TestScheduler`. I think you can think of it as every async action creates a new frame and increments `TestScheduler`'s internal counter. I think every `-` in marble diagrams represented 10 frames but maybe this has changed (maybe to 1?). So the error tells you that emission happened in different times then you expected. – martin Jan 11 '19 at 15:41
  • I think that `-` is equals to 1, which explained by the delay(2) and the expectations logs (there's always a diffrence of 2). By << every async action creates a new frame >> you mean (x, y, z) in the test ? – bubbles Jan 11 '19 at 17:21

1 Answers1

4

To help you better understand what is going on with the output, let's first try to follow statements from the console. There is a link that points at where the error has happened. It's at 10th line of code which is this line:

expect(actual).toEqual(expected);

Setting breakpoint to this line and running the test in debug mode reveals actual and expected objects.

actual values are (represented in JSON format):

[
  {
    "frame": 3,
    "notification": {"kind": "N", "value": "x", "hasValue": true}
  },
  {
    "frame": 5,
    "notification": {"kind": "N", "value": "y", "hasValue": true}
  },
  {
    "frame": 7,
    "notification": {"kind": "N", "value": "z", "hasValue": true}
  },
  {
    "frame": 8,
    "notification": {"kind": "C", "hasValue": false}
  }
]

And the expected:

[
  {
    "frame": 1,
    "notification": {"kind": "N", "value": "x", "hasValue": true}
  },
  {
    "frame": 3,
    "notification": {"kind": "N", "value": "y", "hasValue": true}
  },
  {
    "frame": 5,
    "notification": {"kind": "N", "value": "z", "hasValue": true}
  },
  {
    "frame": 6,
    "notification": {"kind": "C", "hasValue": false}
  }
]

Comparing the two arrays, you can see that frame properties are different for each object of the same index. This weird output comes from Jasmine's toEqual function, so let's try to understand it based on the values from above. This line from the console

Expected $[0].frame = 3 to equal 1.

means that expected value of 1 is not 1, but is actually 3. This part $[0].frame = 3 suggests what the actual value is, and this to equal 1 is what you as developer think it should be. I.e. expected[0].frame (which is 1) is not equal to actual[0].frame (which is 3). And so on, expected[1].frame is not equal to actual[1].frame...

Now, you may wonder why do you even get such values for actual and expected. This is explained much more in detail on the official docs. When you create cold observable with this marble diagram -x-y-z| and delay it with 2 units, it becomes ---x-y-z| which is then transformed to something comparable - an actual array. The first three - signs indicate three empty, non-emitting frames. They are at the positions 0, 1 and 2. They do not have representations in any of the two arrays.

Then comes the first real value x. It is represented as the first object in actual array (actual[0]). x is at position 3 thus the frame property has the same value. notification property has some meta data like the value of the emitted item. You can conclude values for y and z the same way.

Side note: when using run() method, frames become values of 1, 2, 3, etc. instead of 10, 20, 30 when not using run(), for legacy reasons.

Mladen
  • 2,070
  • 1
  • 21
  • 37