cartant / rxjs-marbles

An RxJS marble testing library for any test framework
https://cartant.github.io/rxjs-marbles/
MIT License
300 stars 18 forks source link

How to use marbles and fakeSchedulers together #62

Closed simoneb closed 5 years ago

simoneb commented 5 years ago

Hi,

I would like to be able to create an observable with a marble diagram and then use the fakeSchedulers feature to advance the time manually in my test. I'm using Jasmine with Angular but I haven't been able to get it to work, the observable created with marbles.cold doesn't emit anything when I advance the time using Angular's tick function or the function passed as the first (and sole) argument of the fakeSchedulers function (which btw, I see is deprecated). Also, nesting fakeSchedulers() and marbles() doesn't seem to work. Any idea?

cartant commented 5 years ago

fakeSchedulers is not deprecated. It's useful for testing some observables in situations in which marble testing is either too hard or not possible.

fakeSchedulers is not intended to be used with marble tests. Marble tests use virtual time which - as outlined in the article linked in the README - is a different concept to fake time.

If you really need to test things at intermediate stages of a marble test, I'd suggest evaluating whether or not you really, really do need to do that. If you do, you can use the schedule method to do something like this:

https://github.com/cartant/rxjs-etc/blob/75fc6c97ca3469edb7a314b3174232d3f5d6b90e/source/operators/finalizeWithKind-spec.ts#L14-L23

Essentially, you need to get the TestScheduler to run something at a particular point in virtual time and you can use its schedule method to do just that.

simoneb commented 5 years ago

thanks for the quick answer @cartant, I meant that the argument provided by fakeSchedulers is deprecated, from what I can see here.

Why do you say that testing intermediate stages of an observable is something that I should evaluate whether I really really need it? I mean it looks like a fairly simple scenario to me. I created an example that you can see in action here and copied below for convenience.

I would be interested to hear your thoughts.

describe('marbles', () => {
  it(
    'should allow to control time manually',
    marbles(m => {
      let current

      const q$ = m.cold('---a---b|')

      q$.subscribe(q => (current = q))

      expect(current).toBeUndefined()

      // tick / advance time somehow

      expect(current).toBe('a')

      // tick / advance time somehow

      expect(current).toBe('b')
    })
  )
})
cartant commented 5 years ago

You should not be subscribing. You should let the TestScheduler subscribe. It will do so - implicitly - at the end of the test. Wherever possible you should test against an expected marble diagram. Doing so is simpler and declarative - which is the whole point of marble testing.

simoneb commented 5 years ago

an example would be great, but please imagine that the subscription is not happening in the test, it's happening in a module I'm testing, so I don't have access to the code that subscribes to the observable in the test code, I want to test the way that the module under test behaves when the observable emits

cartant commented 5 years ago

For the TestScheduler to do its thing, it has to subscribe to the hot and cold observables. You cannot expect things to work correctly if explicit subscriptions are made to hot or cold observables.

Unfortunately, I don't have time to imagine various scenarios and post examples of them. Not ATM, anyway.

I would recommend reading the marble testing documentation that's in the RxJS repo - these days, rxjs-marbles is a thin wrapper around TestScheduler#run - and, for examples, look at the marble tests therein. Or at the tests in rxjs-etc.

simoneb commented 5 years ago

@cartant not very helpful but thanks anyway

0xTomDaniel commented 4 years ago

I'd also love to use marbles and fakeSchedulers together. In fact, why can't testSchedulers just be a configuration option for marbles? I have the expectation that if observable virtual time is being faked that JavaScript timers should also be faked simultaneously.

There must be a lot of code in the wild with both JavaScript based timers and observable based timers. Surely @simoneb and I aren't the only one's needing to test such code.

simoneb commented 4 years ago

Surely @simoneb and I aren't the only one's needing to test such code.

When I review Angular codebases, I often see test files to only contain the boilerplate generated by the Angular scaffold, so I'm not terribly surprised ;)

0xTomDaniel commented 4 years ago

Surely @simoneb and I aren't the only one's needing to test such code.

When I review Angular codebases, I often see test files to only contain the boilerplate generated by the Angular scaffold, so I'm not terribly surprised ;)

I hope they have fun fixing regression bugs xD