Dealing with Promises
See original GitHub issueFirst, thanks for the lib! I’ve successfully written several tests, and it’s very effective.
I hit a wall recently though when attempting to test an observable that mergeMap
s a Promise (in my case, one that waits on a network request, which I’ve stubbed in the test with nock
).
Simplified example:
it('promises', marbles(m => {
const obs1 = Observable.from(['a','b','c']).mergeMap(x => Observable.of(x))
m.equal(obs1, '(abc|)') // Works
const obs2 = Observable.from(['a','b','c']).mergeMap(x => Promise.resolve(x))
m.equal(obs2, '(abc|)') // Fails
}))
The second assertion fails with:
Error:
Expected
to deep equal
{"frame":0,"notification":{"kind":"N","value":"a","hasValue":true}}
{"frame":0,"notification":{"kind":"N","value":"b","hasValue":true}}
{"frame":0,"notification":{"kind":"N","value":"c","hasValue":true}}
{"frame":0,"notification":{"kind":"C","hasValue":false}}
I assume this is because Promises (the native implementation, anyway) always resolves on the next tick, while marble testing is intended to test the observable within one tick.
Is my only option to do something hacky like mock the function that returns a promise to return a Observable.of()
stubbed value instead?
Issue Analytics
- State:
- Created 6 years ago
- Reactions:2
- Comments:9 (6 by maintainers)
Top Results From Across the Web
Dealing with Promises in JavaScript | by Travis Waith-Mair
Once again it's important to emphasize that Promises represent asynchronous action. It's called a promise because it “promises” to resolve or ...
Read more >Using promises - JavaScript - MDN Web Docs
Essentially, a promise is a returned object to which you attach callbacks, instead of passing callbacks into a function.
Read more >JavaScript Promise Tutorial – How to Resolve or Reject ...
Just to review, a promise can be created with the constructor syntax, like this: let promise = new Promise(function(resolve, reject) ...
Read more >JavaScript Promises: an introduction - web.dev
Promises simplify deferred and asynchronous computations. A promise represents an operation that hasn't completed yet.
Read more >Deal with Promises like a Pro - DEV Community
The Promise.all takes an iterable of promises as input and then returns a single promise that'll resolve into an array of the results...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Yep. You are correct.
The problem is that promises always resolve asynchronously, so your mock is still asynchronous and not compatible with the
TestScheduler
- which runs the subscriptions in a synchronous manner when itsflush
method is called (usually at the end of each test). That’s why theExpected
output is empty - the test has finished before the promise resolved.You are also correct in that the solution would involve mocking the promise with something the
TestScheduler
understands and can treat in a synchronous manner (either a synchronous observable - likeObservable.of
- or an asynchronous observable that takes theTestScheduler
instance as a parameter).Being able to use marble tests with observables that cannot be made to use virtual time is something that I have on my list of problems to solve, but I’ve not yet devoted any time to it. I have a large number of mocked Firebase tests - written about 12 months ago - that would have been much easier to write using marble tests. Finding a solution is something that will likely wait until I again need to write a substantial number of tests for non-virtual-time observables.
Ok, I think I was looking more for direction in how asynchronous stuff is handled within the marble callback. In jest it still supports returning promises. So this is exactly what I needed.
Hope this helps someone else.