avatarKevin Kreuzer

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

4688

Abstract

<i>Do you want to take your Angular, RxJS, TypeScript, and JavaScript skills to the next level? Don’t miss the chance to check out my <a href="https://www.youtube.com/channel/UCFT4YVZl7AFia7rZBTEvavw">Youtube channel</a>.</i></p></blockquote><figure id="3f29"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/0*mJife3qEGtOZWo00.png"><figcaption><a href="https://www.youtube.com/channel/UCFT4YVZl7AFia7rZBTEvavw">https://www.youtube.com/channel/UCFT4YVZl7AFia7rZBTEvavw</a></figcaption></figure><h1 id="b074">Marble test a subject 🧪</h1><p id="450b">To get started, we first instantiate our <code>sut</code>(system under test) and the <code>TestScheduler</code>.</p><figure id="3c7f"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*rKbW_TxEfvOonjYSaZBLzA.png"><figcaption>Instantiate the class to test and the TestScheduler in a beforeEach hook</figcaption></figure><p id="e4c6">When instantiating the <code>TestScheduler</code> we pass a <code>assertDeppEqual</code> function which tells the <code>TestScheduler</code> how to compare values. From here on, we can start using the <code>TestScheduler</code> to write our marble test.</p><figure id="2df9"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*czpN5BEIz9h2BJ2CPiayPw.png"><figcaption></figcaption></figure><p id="ac95">We want to test that the correct Pizza is emitted by our <code>pizza$</code> Subject.</p><p id="81f6">We use destructuring to get a hold of the <code>TestScheduler’s</code> <code>expectObservable</code> and <code>cold</code> helper methods. We then use the <code>expectObservable</code> function in combination with the Marble syntax to ensure that our expected <i>Observable</i> is an <i>Observable</i> that streams one value, which is “Tonno.”</p><p id="4348">Easy, isn’t it? Let’s run it.</p><div id="0e15"><pre><span class="hljs-keyword">Error: </span>expect(received).toEqual(expected) // deep equality</pre></div><div id="e36a"><pre><span class="hljs-bullet">-</span> Expected - 11 <span class="hljs-bullet">+</span> Received + 1</pre></div><div id="1858"><pre>- <span class="hljs-built_in">Array</span> [

  • <span class="hljs-built_in">Object</span> {
  • <span class="hljs-string">"frame"</span>: <span class="hljs-number">0</span>,
    
  • <span class="hljs-string">"notification"</span>: Notification {
    
  •   <span class="hljs-string">"error"</span>: <span class="hljs-literal">undefined</span>,
    
  •   <span class="hljs-string">"hasValue"</span>: <span class="hljs-literal">true</span>,
    
  •   <span class="hljs-string">"kind"</span>: <span class="hljs-string">"N"</span>,
    
  •   <span class="hljs-string">"value"</span>: <span class="hljs-string">"Tonno"</span>,
    
  • },
    
  • },
  • ]
  • <span class="hljs-built_in">Array</span> []</pre></div><p id="f9c3">Oh no. 😲 Our test fails. What happened?</p><p id="9cc9" type="7">If you like this blog post and want to get updated about new cool things that happen in modern frontend development — follow me on Twitter.</p><p id="5440">Let’s analyze this. The error message says that we expected a value of “Tonno” to be emitted at <code>frame</code> 0, but we didn’t receive any value. Why is that?</p><p id="e271">Let’s take a closer look at the function calls and the order in which they are executed.</p><ol><li>We call the <code>orderPizza</code> function in our test</li></ol><div id="cbbb"><pre>sut.orderPizza(pizzaTonno)<span class="hljs-comment">;</span></pre></div><p id="da2f">2. The <code>pizzaTonno</code> gets “nexted” into our <code>Subject</code>.</p><div id="6180"><pre><span class="hljs-keyword">this</span>.pizza.<span class="hljs-keyword">next</span>(pizza);</pre></div><p id="0fa4">3. Our test subscribes to the exposed <code>Subject</code></p><div id="0531"><pre><span class="hljs-built_in">expectObservable</span>(sut.pizza)<span class="hljs-selector-class">.toBe</span>('a', {a: pizzaTonno});</pre></div><p id="1584">From this order, we can see that it's a timing issue. The value is “nexted” into the <code>Subject</code> before it’s subscribed. After we subscribed, no value is streamed. Therefore we receive an empty array.</p><p id="4b5f">We need to bridge the gap between the timing of function calls and Observables.</p><h1 id="8a1e">Bridging the gap 🌉</h1><p id="131d">To adjust this test, we need to change the timing of the <code>orderPizza</code> function call. Instead of calling it immediately, we need to call it in the same frame as the subscription.</p><p id="be3e">To do so, we can use the <code>TestScheduler’s</code> <code>cold</code> helper method. As the name indicates, the <code>cold</code> function creates a cold Observable from a marble Diagram. We

Options

can <code>subscribe</code> to such an Observable and call the <code>orderPizza</code> function.</p><div id="9b07"><pre><span class="hljs-function"><span class="hljs-title">cold</span><span class="hljs-params">(<span class="hljs-string">'-a'</span>)</span></span><span class="hljs-selector-class">.subscribe</span>(() => sut<span class="hljs-selector-class">.orderPizza</span>(pizzaTonno))</pre></div><p id="22a3">Our complete test then looks like this:</p><figure id="05bd"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/1*zYvjmECLcgeA7R68tPfLAw.png"><figcaption>✅ Successfull marble test a RxJs Subject</figcaption></figure><p id="4e6b">We bridged the gap, and our test is now green. We successfully marble tested a simple service with a Subject.</p><p id="7dc4">For sure, in this simple example, it would also be possible to use the “<i>subscribe and assert</i>” pattern. But then we would be forced to deal with the subscription and the <code>done</code> callback.</p><p id="bd59">It’s up to you to decide which approach is more concise and easier to understand.</p><h1 id="eb86">Further resources about Marble testing</h1><p id="3282">If you want to get to know more in-depth detail on marble testing and the <code>TestScheduler</code> you may be interested in one of my articles about RxJS and testing.</p><figure id="0bad"><img src="https://cdn-images-1.readmedium.com/v2/resize:fit:800/0*69UGkHudQrM5LFsd.png"><figcaption></figcaption></figure><p id="cc03">Welcome to the world of Angular excellence — <a href="https://www.angularexperts.ch/">angularexperts.ch</a></p><blockquote id="d152"><p><b><i>Do you find the information in this article useful? — </i></b><i>We are providing tailored expert support for developing of your Angular applications. Explore our wide range offers on <a href="https://www.angularexperts.ch/">angularexperts.ch</a></i></p></blockquote><div id="efae" class="link-block"> <a href="https://readmedium.com/how-to-test-observables-a00038c7faad"> <div> <div> <h2>How to test Observables</h2> <div><h3>The ultimate guide — never again be confused on how to test Observables</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*owGWewkhtBP_xlXEoGXBog.png)"></div> </div> </div> </a> </div><div id="28a5" class="link-block"> <a href="https://readmedium.com/marble-testing-with-rxjs-testing-utils-3ae36ac3346a"> <div> <div> <h2>Marble testing with RxJS testing utils</h2> <div><h3>You don’t need a third-party library for marble testing</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*bjcmm2W0TSLjwfJPbcKaIA.png)"></div> </div> </div> </a> </div><div id="a810" class="link-block"> <a href="https://readmedium.com/testing-asynchronous-rxjs-operators-5495784f249e"> <div> <div> <h2>Testing asynchronous RxJs operators</h2> <div><h3>Time traveling with virtual time and time progression syntax</h3></div> <div><p>medium.com</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*HcDg2e_MrY2WbdFNX5G3zA.png)"></div> </div> </div> </a> </div><div id="96b7" class="link-block"> <a href="https://itnext.io/findings-about-rxjs-marble-testing-and-the-testscheduler-b23c6bdf6b49"> <div> <div> <h2>Findings about RxJS marble testing and the TestScheduler</h2> <div><h3>Recently I wrote a custom Rx Operator that retries failed http request. The way RxJS allows us to deal with such async…</h3></div> <div><p>itnext.io</p></div> </div> <div> <div style="background-image: url(https://miro.readmedium.com/v2/resize:fit:320/1*UmYEN7sszbjpqpJJPQioUw.jpeg)"></div> </div> </div> </a> </div><h1 id="b8c6">🧞‍ 🙏 If you liked this post, share it and give some claps👏🏻 by clicking multiple times on the left side's clap button.</h1><p id="bb53"><i>Claps help other people to discover content and motivate me to write more.</i> 😉</p></article></body>

How to marble test RxJS Subjects

How to bridge the gap between the timing of function calls and Observables

Asynchronous code is hard! It’s hard to write and to test.

RxJs has changed the way we think about asynchrony. Instead of using Promises, we nowadays deal with streams in the form of Observables or Subjects. RxJs provides us with many out of the box operators to create, merge, or transform streams.

But we do not only get great tools for runtime code, but we also get amazing tools to test streams.

Marble diagrams are a great way of modeling streams. They are used throughout many tutorials and can now also be used in tests to assert streams.

In case you never heard of marble testing I recommend you to checkout the following article.

Marble testing is great! Many people use it to test their Observables. But what about Subjects?

A pizza service 🍕

Nowadays, most frameworks use the concept of components. When working with components, you often need a way of communication between them. Usually, you have multiple ways of implementing communication between components.

Angular, for example, offers component communication over @Input and @Output, communication over a service or, for a more sophisticated use case, the use of a state management library such as ngrx.

For simple use cases, a service with a Subject is often a nice solution. Imagine an app where you can order Pizzas.

A simple pizza app that uses a Service with a Subject to communicate between components.

A pizza can be ordered by clicking a button on a list item. The button click then uses the OrderService to inform the shopping cart that a new pizza has been added. Let’s have a look at the code of the OrderService.

Our OrderService has a public orderPizza method that will next a new Pizza into our pizza$.

☝ ️You should never expose a Subject directly. It’s always better to only expose an Observable. To do so you can always use the asObservable method of a Subject. To keep things as simple as possible and focus on the testing Problem we leave that away for now.

Okay cool. We implemented an impeccable service. Let’s test it. 😃

There are multiple ways to test such services. We can use the classic “subscribe and assert” pattern or “marble testing.” If you want to find out more about their differences and which you should use, I recommend you to check out this article:

We are interested in marble testing this service.

Do you want to take your Angular, RxJS, TypeScript, and JavaScript skills to the next level? Don’t miss the chance to check out my Youtube channel.

https://www.youtube.com/channel/UCFT4YVZl7AFia7rZBTEvavw

Marble test a subject 🧪

To get started, we first instantiate our sut(system under test) and the TestScheduler.

Instantiate the class to test and the TestScheduler in a beforeEach hook

When instantiating the TestScheduler we pass a assertDeppEqual function which tells the TestScheduler how to compare values. From here on, we can start using the TestScheduler to write our marble test.

We want to test that the correct Pizza is emitted by our pizza$ Subject.

We use destructuring to get a hold of the TestScheduler’s expectObservable and cold helper methods. We then use the expectObservable function in combination with the Marble syntax to ensure that our expected Observable is an Observable that streams one value, which is “Tonno.”

Easy, isn’t it? Let’s run it.

Error: expect(received).toEqual(expected) // deep equality
- Expected  - 11
+ Received  +  1
- Array [
-   Object {
-     "frame": 0,
-     "notification": Notification {
-       "error": undefined,
-       "hasValue": true,
-       "kind": "N",
-       "value": "Tonno",
-     },
-   },
- ]
+ Array []

Oh no. 😲 Our test fails. What happened?

If you like this blog post and want to get updated about new cool things that happen in modern frontend development — follow me on Twitter.

Let’s analyze this. The error message says that we expected a value of “Tonno” to be emitted at frame 0, but we didn’t receive any value. Why is that?

Let’s take a closer look at the function calls and the order in which they are executed.

  1. We call the orderPizza function in our test
sut.orderPizza(pizzaTonno);

2. The pizzaTonno gets “nexted” into our Subject.

this.pizza$.next(pizza);

3. Our test subscribes to the exposed Subject

expectObservable(sut.pizza$).toBe('a', {a: pizzaTonno});

From this order, we can see that it's a timing issue. The value is “nexted” into the Subject before it’s subscribed. After we subscribed, no value is streamed. Therefore we receive an empty array.

We need to bridge the gap between the timing of function calls and Observables.

Bridging the gap 🌉

To adjust this test, we need to change the timing of the orderPizza function call. Instead of calling it immediately, we need to call it in the same frame as the subscription.

To do so, we can use the TestScheduler’s cold helper method. As the name indicates, the cold function creates a cold Observable from a marble Diagram. We can subscribe to such an Observable and call the orderPizza function.

cold('-a').subscribe(() => sut.orderPizza(pizzaTonno))

Our complete test then looks like this:

✅ Successfull marble test a RxJs Subject

We bridged the gap, and our test is now green. We successfully marble tested a simple service with a Subject.

For sure, in this simple example, it would also be possible to use the “subscribe and assert” pattern. But then we would be forced to deal with the subscription and the done callback.

It’s up to you to decide which approach is more concise and easier to understand.

Further resources about Marble testing

If you want to get to know more in-depth detail on marble testing and the TestScheduler you may be interested in one of my articles about RxJS and testing.

Welcome to the world of Angular excellence — angularexperts.ch

Do you find the information in this article useful? — We are providing tailored expert support for developing of your Angular applications. Explore our wide range offers on angularexperts.ch

🧞‍ 🙏 If you liked this post, share it and give some claps👏🏻 by clicking multiple times on the left side's clap button.

Claps help other people to discover content and motivate me to write more. 😉

Rxjs
Angular
Typescript
Testing
JavaScript
Recommended from ReadMedium