<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Andrew Scott on Medium]]></title>
        <description><![CDATA[Stories by Andrew Scott on Medium]]></description>
        <link>https://medium.com/@atscott_angular?source=rss-3a9cafe0f566------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*280lqQqDAyswE-MC</url>
            <title>Stories by Andrew Scott on Medium</title>
            <link>https://medium.com/@atscott_angular?source=rss-3a9cafe0f566------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 20:40:20 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@atscott_angular/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Handling Time and Mock Clocks in Tests]]></title>
            <link>https://blog.angular.dev/handling-time-and-mock-clocks-in-tests-5a393b32dd30?source=rss-3a9cafe0f566------2</link>
            <guid isPermaLink="false">https://medium.com/p/5a393b32dd30</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[software-testing]]></category>
            <dc:creator><![CDATA[Andrew Scott]]></dc:creator>
            <pubDate>Fri, 07 Nov 2025 01:01:24 GMT</pubDate>
            <atom:updated>2025-11-07T01:01:24.721Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="Handling time and mock clocks in tests" src="https://cdn-images-1.medium.com/max/1024/1*Ghd6Y89C63bDJfVxiHTl6Q.png" /></figure><p>In the realm of software testing, the concept of time can be a tricky one to manage, especially when dealing with asynchronous operations and timeouts. We are continually refining our approach to handling time in unit tests, particularly concerning the use of mock clocks. We’ve found that mock clocks, while useful, often lead to headaches in test code and unrealistic timing scenarios. To address these challenges, we are exploring the exciting concept of “auto-advancing” mock clocks, which aim to streamline testing and enhance the accuracy of time-based simulations. In this blog post, we’ll delve into the intricacies of mock clocks, their limitations, and the potential benefits of auto-advancing functionality.</p><h3>Why use a mock clock</h3><p>We see a few reasons to install mock clocks in tests.</p><ol><li>To <strong>fake the date</strong>. This makes it possible to test components which display the current time or date related information, or do some calculation with respect to the current time.</li><li>More frequently, to <strong>speed up time</strong>. UIs are frequently written with debounces, visible delays for users, timeouts, etc. We want to verify the state of the app after these long-running tasks complete but we don’t want the test to actually wait 30 seconds for a timeout. Instead, mock clocks can fake the progression of time without needing to modify the production code to expose some overridable timeout simply to make the test quicker.</li><li>To <strong>prevent timers from leaking into other tests</strong>, creating <a href="https://en.wikipedia.org/wiki/Action_at_a_distance_(computer_programming)">action at a distance</a> errors. Mock clocks can uninstall after each test and either dispose of or flush any pending timers.</li><li>To <strong>execute all pending timers</strong> or stabilize the test. This allows the test to say, “wait until everything that’s supposed to happen has happened”. This can be useful for several reasons, including <strong>to verify that something isn’t going to happen</strong>. For example, consider testing Gmail’s “undo” send button. After clicking the button, you would want to verify that the RPC to send the email was never sent. We can also verify that there is no pending work. If the test finishes while there is more asynchronous work pending, we don’t want the pending work to continue executing during the next test case. (It’s hopelessly confusing.) The test should either (1) wait for the work to finish, (2) dispose() of the workers explicitly, or (3, least preferred) ask the fake clock to discard pending timers/frames.</li><li>Much less common is to <strong>make time progress in specific increments</strong> in a unit test. One example might be a toast which appears after a known delay, and disappears after another known delay. This use of mock clocks, however, is not really a separate point from the second. We already have the ability to tick the clock specific amounts of time with timeouts. It’s just slow and mock clocks provide nice APIs to speed it up. Mock clocks <em>can</em> make these specific increments more predictable in tests. When using real timeouts, the ordering can sometimes depend on how quickly each task can be processed.</li></ol><p>These are pretty good reasons to use a fake clock, and pretty broadly useful when async code is in play. We do support the idea of installing a fake clock.</p><h3>Common pitfalls with mock clocks</h3><p>While mock clocks offer a degree of control over time in tests, they can also introduce challenges and anti-patterns, and these all apply equally to Angular’s fakeAsync test helper.</p><p>Before the widespread availability and adoption of Promises and async/await in javascript, mock clocks helped keep tests manageable by making them synchronous. In modern-day javascript, we no longer need this. Furthermore, native Promises, async-await, resize or mutation observers, RPCs, etc cannot be executed synchronously; it is impossible to write a synchronous test for such logic. Tests with mock clocks are often forced into a situation where they need to both tick the mocked time <em>and</em> wait for real asynchronous code to happen. This can lead to unrealistic execution orders and can mask timing-related bugs. As a tester, keeping track of this can become a real nightmare and as a result, we’ve seen plenty of “big hammer” helper functions in tests:</p><pre>async function realFlush(delay?: number) {<br>  clock.flush();<br>  await new Promise&lt;void&gt;(resolve =&gt; void setTimeout(resolve, delay));<br>  clock.flush();<br>}</pre><p>Tests also become littered with hard-coded wait times, sometimes with purpose, but often somewhat arbitrary, such as mockClock.tick(1000) or mockClock.flushPendingTimers. Not only do these ticks and flushes often lack clear justification, but they also may need to be adjusted as the codebase evolves. Adjusting a timer in the production code can cause tests to fail, including many which were not intended to depend on or observe the specific timing implementation. In many ways, mock clocks can turn these sections of the tests into <a href="https://testing.googleblog.com/2015/01/testing-on-toilet-change-detector-tests.html">change detector tests</a>.</p><p>Finally, some test utilities take it upon themselves to tick the mock clock, potentially interfering with the test’s intended timing and causing unexpected behavior. On the flipside, asynchronous test helpers and harnesses need to be informed about any installed mock clocks and how to advance them. Forgetting to provide this (assuming the testing APIs even have an option to do so, and many do not) will cause the test helper to stall, waiting for time that never advances.</p><h3>Proposal: Allow mock clocks to automatically advance time</h3><p>In the majority of use cases, mock clocks are used to speed up time. In order to do this, testers are opting into behavior that <em>also </em>stops time. We suspect that the majority of tests don’t want or need this. Clocks don’t stop in real life so it would make sense for mock clocks to advance on their own as well, unless there is a specific desire to pause it.</p><p>If mock clocks were able to advance time in a realistic way then we could realize a future where: <em>No one should care whether time is being faked (unless they want to).</em></p><p>This includes business logic, test helpers, and even most tests themselves. It would be easy to migrate to a mock clock implementation with this property. That is, it would be ideal to be able to take a test that is written against real timer APIs and install a mock clock with no other changes to the tests. Auto-advance gets us pretty close.</p><h3>Use cases</h3><p>Tests with mock clocks would look identical to those without. Let’s go through some examples:</p><h4>Testing async functions</h4><p>When testing async functions, tests only need to enable auto advancing:</p><pre>// methods_under_test.ts<br>export async function blackBoxWithLotsOfAsyncStuff() {<br>  // do some work<br>  await new Promise(r =&gt; void setTimeout(r, 10));<br>  // do some more work<br>  await new Promise(r =&gt; void setTimeout(r, 20));<br>  await Promise.resolve();<br>  // yay, we&#39;re done!<br>  return &#39;done&#39;;<br>}<br>// methods_under_test.spec.ts<br>// …<br>beforeEach(() =&gt; clock.setTickMode(&#39;auto&#39;));<br>it(&#39;is easy to test async functions with interleaved timers and microtasks&#39;, async () =&gt; {<br>  const result = await blackBoxWithLotsOfAsyncStuff();<br>  expect(result).toBe(&#39;done&#39;);<br>});<br>// …</pre><h4>Waiting for conditions to be satisfied</h4><p>The specific timeframe that conditions occur is usually best thought of as an “implementation detail”, rather than part of the public API commitments. It’s fairly easy to imagine changes in the future that alter the timeframe, and which are completely invisible to the user (and the test!).</p><p>For example, changing a debounce time on an instant search input from 100ms to 200ms. Tests which use mock clocks today would typically trigger the input, <strong>explicitly tick</strong> (100 or 200ms), and then make assertions. Instead, tests would use a polling API to <strong>explicitly wait</strong> for a condition to be satisfied as a result of the input changing, such as the <a href="https://testing-library.com/docs/dom-testing-library/api-async/">Testing Library’s async methods</a>:</p><pre>beforeEach(() =&gt; clock.setTickMode(&#39;auto&#39;));<br>it(&#39;gets results when the search changes&#39;, () =&gt; {<br>  const input = screen.getByLabelText(&#39;search-input&#39;);<br>  fireEvent.change(input, {target: {value: &#39;signals&#39;}});<br>  await waitFor(() =&gt; expect(mockAPI).toHaveBeenCalledTimes(1));<br>  await screen.findByText(&#39;Computed signals&#39;);<br>  fireEvent.change(input, {target: {value: &#39;control flow&#39;}});<br>  await screen.findByText(&#39;Control Flow in Components - @if&#39;);<br>});</pre><h3>What’s next</h3><p>We started working with test framework authors to support automatically advancing time in mock clock implementations. As of Jasmine v5.7, the clock now has the ability to advance automatically and can be enabled with <a href="https://jasmine.github.io/api/5.7/Clock#autoTick">Clock#autoTick</a>. It is also released in <a href="https://www.npmjs.com/package/@sinonjs/fake-timers/v/15.0.0#user-content-clocksettickmodemode">@sinonjs/fake-timers version 15</a>, which is used by <a href="https://github.com/jestjs/jest/commit/6813dfa8c78f0232726db88e31c04780d785b3bf">Jest</a> and Vitest.</p><pre>// jasmine v5.7<br>jasmine.clock().install().autoTick();<br><br>// @sinonjs/fake-timers v15<br>const clock = FakeTimers.install();<br>clock.setTickMode({mode: &#39;nextAsync&#39;});<br><br>// future Jest release<br>jest.useFakeTimers().setTimerTickMode({mode: &#39;nextAsync&#39;});</pre><p>Follow us on social media for updates. We hope that this feature is as exciting to the community as it is to us. An automatically advancing mock clock could help existing async tests be faster and more predictable, regardless of what web framework is being used, since this feature doesn’t require any other changes to the test after it’s enabled.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5a393b32dd30" width="1" height="1" alt=""><hr><p><a href="https://blog.angular.dev/handling-time-and-mock-clocks-in-tests-5a393b32dd30">Handling Time and Mock Clocks in Tests</a> was originally published in <a href="https://blog.angular.dev">Angular Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Check out Angular’s support for the View Transitions API]]></title>
            <link>https://blog.angular.dev/check-out-angulars-support-for-the-view-transitions-api-3937376cfc19?source=rss-3a9cafe0f566------2</link>
            <guid isPermaLink="false">https://medium.com/p/3937376cfc19</guid>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[angular-router]]></category>
            <category><![CDATA[angular-renaissance]]></category>
            <category><![CDATA[view-transition]]></category>
            <category><![CDATA[view-transitions-api]]></category>
            <dc:creator><![CDATA[Andrew Scott]]></dc:creator>
            <pubDate>Wed, 13 Dec 2023 20:35:59 GMT</pubDate>
            <atom:updated>2024-05-16T04:24:45.084Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="Two colorful web pages representing a transition" src="https://cdn-images-1.medium.com/max/1024/0*CWTw788ACpkIvM67" /><figcaption>Two colorful web pages representing a transition</figcaption></figure><p>When a user navigates from one route to another, the Angular Router maps the URL path to the relevant component and displays its view. Animating this route transition can greatly enhance the user experience. In version 17, the Router now has support for the <a href="https://developer.chrome.com/docs/web-platform/view-transitions/">View Transitions API</a> when navigating between routes in Chrome/Chromium browsers.</p><h3>How View Transitions work</h3><p>The native browser method that’s used for view transitions is document.startViewTransition. When startViewTransition() is called, the browser captures the current state of the page which includes taking a screenshot. The method takes a callback that updates the DOM and this function can be asynchronous. The new state is captured and the transition begins in the next animation frame when the promise returned by the callback resolves.</p><p>Here’s an example of the startViewTransition api:</p><pre>document.startViewTransition(async () =&gt; {<br>  await updateTheDOMSomehow();<br>});</pre><p>If you’re curious to read more about the details of the browser API, the <a href="https://developer.chrome.com/docs/web-platform/view-transitions">Chrome Explainer</a> is an invaluable resource.</p><h3>How the Router uses view transitions</h3><p>Several things happen after navigation starts in the router: route matching, loading lazy routes and components, executing guards and resolvers to name a few. Once these have completed successfully, the new routes are ready to be activated. This route activation is the DOM update that we want to perform as part of the view transition.</p><p>When the view transition feature is enabled, navigation “pauses” and a call is made to the browser’s startViewTransition method. Once the startViewTransition callback executes (this happens asynchronously, as outlined in the spec <a href="https://drafts.csswg.org/css-view-transitions-1/#callbackdef-updatecallback">here</a>), navigation “resumes”. The remaining steps for the router navigation include updating the browser URL and activating or deactivating the matched routes (the DOM update).</p><p>Finally, the callback passed to startViewTransition returns a Promise that resolves once Angular has finished rendering. As described above, this indicates to the browser that the new DOM state should be captured and the transition should begin.</p><p>View transitions are a <a href="https://developer.mozilla.org/en-US/docs/Glossary/Progressive_Enhancement">progressive enhancement</a>. If the browser does not support the API, the Router will perform the DOM updates without calling startViewTransition and the navigation will not be animated.</p><h3>Enabling view transitions in the router</h3><p>To enable this feature add withViewTransitions to the provideRouter function:</p><pre>bootstrapApplication(MyApp, {providers: [<br>  provideRouter(routes, withViewTransitions()),<br>]});</pre><p><a href="https://stackblitz.com/edit/stackblitz-starters-2dnvtm">Try the “count” example on StackBlitz</a></p><p>This example uses the counter application from the <a href="https://developer.chrome.com/docs/web-platform/view-transitions/#working-with-frameworks">Chrome explainer</a> and replaces the direct call to startViewTransition when the counter increments with a router navigation.</p><h3>Using CSS to customize transitions</h3><p>View transitions can be customized with CSS. We can also instruct the browser to create separate elements for the transition by setting a view-transition-name. We can expand the first example by adding view-transition-name: count to the .count style in the Counter component. Then, in the global styles, we can define a custom animation for this view transition:</p><pre>/* Custom transition */<br>@keyframes rotate-out {<br> to {<br>   transform: rotate(90deg);<br> }<br>}<br>@keyframes rotate-in {<br> from {<br>   transform: rotate(-90deg);<br> }<br>}<br>::view-transition-old(count),<br>::view-transition-new(count) {<br> animation-duration: 200ms;<br> animation-name: -ua-view-transition-fade-in, rotate-in;<br>}<br>::view-transition-old(count) {<br> animation-name: -ua-view-transition-fade-out, rotate-out;<br>}</pre><p>It is important that the view transition animations are defined in a global style file. They cannot be defined in the component styles because the default view encapsulation will scope the styles to the component.</p><p><a href="https://stackblitz.com/edit/stackblitz-starters-fwn4i7">Try the updated “count” example on StackBlitz</a></p><h3>Controlling transitions with onViewTransitionCreated</h3><p>The withViewTransitions router feature can also be called with an options object that includes an onViewTransitionCreated callback. This callback is run in an <a href="https://angular.dev/guide/di/dependency-injection-context#run-within-an-injection-context">injection context</a> and receives a <a href="https://angular.dev/api/router/ViewTransitionInfo">ViewTransitionInfo object</a> that includes the ViewTransition returned from startViewTransition, as well as the ActivatedRouteSnapshot that the navigation is transitioning from and the new one that it is transitioning to.</p><p>This callback can be used for any number of customizations. For example, you might want to skip transitions under certain conditions. We use this on the new <a href="https://angular.dev/">angular.dev</a> docs site:</p><pre>withViewTransitions({<br> onViewTransitionCreated: ({transition}) =&gt; {<br>   const router = inject(Router);<br>   const targetUrl = router.getCurrentNavigation()!.finalUrl!;<br>   // Skip the transition if the only thing <br>   // changing is the fragment and queryParams<br>   const config = { <br>     paths: &#39;exact&#39;, <br>     matrixParams: &#39;exact&#39;,<br>     fragment: &#39;ignored&#39;,<br>     queryParams: &#39;ignored&#39;,<br>   };<br><br>   if (router.isActive(targetUrl, config)) {<br>     transition.skipTransition();<br>   }<br> },<br>}),</pre><p>In this code snippet, we create a UrlTree from the ActivatedRouteSnapshot the navigation is going to. We then check with the Router to see if this UrlTree is already active, ignoring any differences in the fragment or query parameters. If it is already active, we call skipTransition which will skip the animation portion of the view transition. This is the case when clicking on an anchor link that will only scroll to another location in the same document.</p><h3>Examples from the Chrome explainer adapted to Angular</h3><p>We’ve recreated some of the great examples from the Chrome Team in Angular for you to explore.</p><p><strong>Transitioning elements don’t need to be the same DOM element</strong></p><ul><li><a href="https://developer.chrome.com/docs/web-platform/view-transitions/same-document#transitioning_elements_dont_need_to_be_the_same_dom_element">Chrome Explainer</a></li><li><a href="https://stackblitz.com/edit/stackblitz-starters-dh8npr?file=src%2Fmain.ts">Angular Example on StackBlitz</a></li></ul><p><strong>Custom entry and exit animations</strong></p><ul><li><a href="https://developer.chrome.com/docs/web-platform/view-transitions/same-document#custom_entry_and_exit_transitions">Chrome Explainer</a></li><li><a href="https://stackblitz.com/edit/stackblitz-starters-8kly3o">Angular Example on StackBlitz</a></li></ul><p><strong>Async DOM updates and waiting for content</strong></p><ul><li><a href="https://developer.chrome.com/docs/web-platform/view-transitions/same-document#async_dom_updates_and_waiting_for_content">Chrome Explainer</a></li></ul><blockquote>During this time, the page is frozen, so delays here should be kept to a minimum…in some cases it’s better to avoid the delay altogether, and use the content you already have.</blockquote><p>The view transition feature in the Angular router does not provide a way to delay the animation. For the moment, our stance is that it’s always better to use the content you have rather than making the page non-interactive for any additional amount of time.</p><p><strong>Handle multiple view transition styles with view transition types</strong></p><ul><li><a href="https://developer.chrome.com/docs/web-platform/view-transitions/same-document#view-transition-types">Chrome Explainer</a></li><li><a href="https://stackblitz.com/edit/stackblitz-starters-fwhphm">Angular Example on StackBlitz</a></li></ul><p><strong>Handle multiple view transition styles with a class name on the view transition root (deprecated)</strong></p><ul><li><a href="https://developer.chrome.com/docs/web-platform/view-transitions/same-document#changing-on-navigation-type">Chrome Explainer</a></li><li><a href="https://stackblitz.com/edit/stackblitz-starters-nmnzzg">Angular Example on StackBlitz</a></li></ul><p><strong>Transitioning without freezing other animations</strong></p><ul><li><a href="https://developer.chrome.com/docs/web-platform/view-transitions/same-document#transitioning-without-freezing">Chrome Explainer</a></li><li><a href="https://stackblitz.com/edit/stackblitz-starters-76kgww">Angular Example on StackBlitz</a></li></ul><p><strong>Animating with Javascript</strong></p><ul><li><a href="https://developer.chrome.com/docs/web-platform/view-transitions/same-document#animating-with-javascript">Chrome Explainer</a></li><li><a href="https://stackblitz.com/edit/stackblitz-starters-cklnkm">Angular Example on StackBlitz</a></li></ul><h3>Try out this experimental feature today!</h3><p>While Angular’s support for the View Transitions API is still experimental, we’re excited for the possibilities it creates to build the next generation of web experiences for users. The team will continue working to stabilize support and you can help. Try out this feature and leave feedback on GitHub. We’ve included multiple examples for you to try out and have fun with. If you implement something cool, tag us on <a href="https://x.com/angular">X</a> and show us the cool things you create.</p><p>Until the next time thanks for being a part of the Angular community and keep building.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3937376cfc19" width="1" height="1" alt=""><hr><p><a href="https://blog.angular.dev/check-out-angulars-support-for-the-view-transitions-api-3937376cfc19">Check out Angular’s support for the View Transitions API</a> was originally published in <a href="https://blog.angular.dev">Angular Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Write better tests without Router mocks/stubs]]></title>
            <link>https://blog.angular.dev/write-better-tests-without-router-mocks-stubs-bf5fc95c1c57?source=rss-3a9cafe0f566------2</link>
            <guid isPermaLink="false">https://medium.com/p/bf5fc95c1c57</guid>
            <category><![CDATA[angular-15]]></category>
            <category><![CDATA[angular-testing]]></category>
            <category><![CDATA[angular-router]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[routertestharness]]></category>
            <dc:creator><![CDATA[Andrew Scott]]></dc:creator>
            <pubDate>Thu, 02 Mar 2023 20:01:28 GMT</pubDate>
            <atom:updated>2023-03-02T20:01:28.243Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="Dog with a harness on gazing over some rocks at the Oregon coast." src="https://cdn-images-1.medium.com/max/1024/1*_Ow3G137nnafrO0boJXFew.png" /><figcaption>Andrew Scott’s pup gazing at the Oregon Coast</figcaption></figure><h3>Prior advice</h3><p>Conventional wisdom might suggest that test authors should stub external dependencies like <a href="https://v14.angular.io/guide/testing-components-scenarios#testing-with-activatedroutestub">ActivatedRoute</a> or <a href="https://v14.angular.io/guide/testing-components-scenarios#stubbing-unneeded-components">RouterLink</a> and <a href="https://v14.angular.io/guide/testing-components-scenarios#routing-component">spy on services and service methods</a> like navigateByUrl. A <a href="https://v14.angular.io/guide/testing-components-scenarios#what-good-are-these-tests">defense</a> for this testing strategy might be</p><blockquote>“Relying on the real router would make them brittle. They could fail for reasons unrelated to the component.”</blockquote><p>This isn’t entirely true, but for now we will point out one <em>very</em> important detail in this defense that’s often ignored. Per the documentation:</p><blockquote>“A different battery of tests can explore whether the application navigates as expected in the presence of conditions that influence guards such as whether the user is authenticated and authorized.”</blockquote><p>It’s important that there still exists a suite of tests that <em>do</em> cover the resulting behavior of executing the real code of the dependency. Unfortunately, this is most often not done. It creates a burden on developers to maintain additional suites of tests when the unit tests being written with mocks could easily cover these behaviors as well. APIs change over time, so writing and maintaining a complete set of mocks and stubs for all the dependencies is often much more difficult and brittle than just using the real ones.</p><h3>Boilerplate to test with real Router instances</h3><p>Testing components and services that depend on the Angular Router can be difficult. For example, a component might inject <a href="https://angular.io/api/router/ActivatedRoute">ActivatedRoute</a> and only use it to access the Observable for queryParams. Getting the Router to create a real instance of ActivatedRoute for the test would require quite a bit of boilerplate. For example:</p><ol><li>Write a Route for the component under test ( {path: ‘**&#39;, component: MyComponent} )</li><li>Add the Router to TestBed ( TestBed.configureTestingModule)</li><li>Create a wrapper component with a router-outlet to render the component</li><li>Create the test ComponentFixture for the wrapper component instead of MyComponent</li><li>Tell the Router to navigate to the Route with the MyComponent</li><li>Use the wrapper ComponentFixture to query for MyComponent</li></ol><p>It’s not until step 6 that you finally have your component you can use to do test assertions. With all of this complicated boilerplate, it makes sense that test authors might want to create a shortcut and share a test stub for ActivatedRoute and override the injectable in TestBed.</p><h3>RouterTestingHarness to help</h3><p>The <a href="https://angular.io/api/router/testing/RouterTestingHarness">RouterTestingHarness</a> was released in Angular 15.2 to streamline tests for components and services that depend on the Router.</p><pre>// Provide the router with your test route<br>TestBed.configureTestingModule({<br>  providers: [<br>    provideRouter([{path: &#39;&#39;, component: TestCmp}])<br>  ],<br>});<br>// Create the testing harness<br>const harness = await RouterTestingHarness.create();<br>// Navigate to the route to get your component<br>const activatedComponent = await harness.navigateByUrl(&#39;/&#39;, TestCmp);</pre><p>Notice how little boilerplate there is now. There’s no need to define a wrapper component yourself, no creation of the wrapper fixture, and no manual query for your test component. All of this is done behind the scenes in the harness.</p><p>Let’s cover a test scenario and compare the mock/stub and spy approach to what the test looks like with new RouterTestingHarness.</p><h3>Mock ActivatedRoute for accessing query parameters</h3><p>Let’s test a simple component that displays the value of a query parameter named search and provides a method for having the router update that parameter:</p><pre>@Component({<br>  standalone: true,<br>  imports: [AsyncPipe],<br>  template: `search: {{(route.queryParams | async)?.search}}`<br>})<br>class SearchCmp {<br>  constructor(readonly route: ActivatedRoute, readonly router: Router) {}<br>  searchFor(searchText: string) {<br>    return this.router.navigate([], {queryParams: {&#39;search&#39;: searchText}});<br>  }<br>}</pre><p>Now we write a test that ensures the template updates with the current value of the parameter. First, we need to create our stubs:</p><pre>const activatedRouteStub = {queryParams: new BehaviorSubject&lt;any&gt;({})};<br>const routerStub = jasmine.createSpyObj(&#39;router&#39;, [&#39;navigate&#39;]);<br>routerStub.navigate.and.callFake(<br>  (commands, navigationExtras) =&gt;<br>    activatedRouteStub.queryParams.next(navigationExtras.queryParams));</pre><p>Next, we set up the testing module and create our component:</p><pre>TestBed.configureTestingModule({<br>  imports: [SearchCmp],<br>  providers: [<br>    {provide: Router, useValue: routerStub},<br>    {provide: ActivatedRoute, useValue: activatedRouteStub},<br>  ]<br>});<br>const fixture = TestBed.createComponent(SearchCmp);</pre><p>Finally, let’s write a quick test to ensure the template updates with the current search query parameter:</p><pre>await fixture.componentInstance.searchFor(&#39;books&#39;);<br>fixture.detectChanges();<br>expect(fixture.nativeElement.innerHTML).toContain(&#39;search: books&#39;);</pre><h3>Problems</h3><ul><li>If there’s a RouterLink on the page, the RouterLink creates a url from the stub. Stubs are frequently not set up to fully satisfy the interface. Refactors in the Router/RouterLink can cause the test to break if they access new properties that are supposed to exist but don’t on the stub.</li><li>Refactoring the component to access other properties of ActivatedRoute would break the test even though the component code is not broken.</li><li>Refactoring the component to get the parameters in a new way would break the test even though the component code is not wrong.</li></ul><p>Recall the argument from earlier:</p><blockquote>“Relying on the real router would make them brittle.”</blockquote><p>In fact, these tests are <em>more</em> brittle because they rely on implementation details of the component. There’s a good argument that these tests are actually <a href="https://testing.googleblog.com/2015/01/testing-on-toilet-change-detector-tests.html">harmful change detector tests</a>. Refactors in the component implementation that do not affect the end behavior or rendered template but cause the tests to fail arguably indicate you have change detector tests.</p><p>In addition, if the Router behavior changes in a way that changes the outcome of the component’s interactions with the Router API, you would <em>want</em> the test to fail. If it doesn’t fail, the tests don’t catch the difference in behavior, and this can cause a production bug.</p><h3>Using RouterTestingHarness instead</h3><p>We’ll take this same example and convert it to use the new RouterTestingHarness. First, configure the testing module to provide the Router with a route using the SearchCmp:</p><pre>TestBed.configureTestingModule({<br>  providers: [<br>    provideRouter([{path: &#39;**&#39;, component: SearchCmp}])<br>  ],<br>});</pre><p>Next, use the harness to get the component instance:</p><pre>const harness = await RouterTestingHarness.create();<br>const activatedComponent = await harness.navigateByUrl(&#39;/&#39;, SearchCmp);</pre><p>Finally, we make the updates and assertions as before:</p><pre>await activatedComponent.searchFor(‘books’);<br>harness.detectChanges();<br>expect(harness.routeNativeElement?.innerHTML).toContain(&#39;search: books&#39;);</pre><p>We have at least a couple of nice outcomes when writing the test this way:</p><ul><li>We don’t have to write or maintain any test stubs or override providers. The Router executes and we don’t have to worry about rewriting stubs to behave similarly.</li><li>As an example, we could refactor the component to use matrix parameters instead and do not need to update any test stubs.</li></ul><h3>Wrap up</h3><p>Using the RouterTestingHarness rather than mocking or stubbing Router dependencies will make tests more reliable. The example above only outlines one scenario, but there are many more examples where a mock or stub would make tests <em>more</em> brittle and/or hide actual production issues in the application due to upstream Router changes.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bf5fc95c1c57" width="1" height="1" alt=""><hr><p><a href="https://blog.angular.dev/write-better-tests-without-router-mocks-stubs-bf5fc95c1c57">Write better tests without Router mocks/stubs</a> was originally published in <a href="https://blog.angular.dev">Angular Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Advancements in the Angular Router]]></title>
            <link>https://blog.angular.dev/advancements-in-the-angular-router-5d69ec4c032?source=rss-3a9cafe0f566------2</link>
            <guid isPermaLink="false">https://medium.com/p/5d69ec4c032</guid>
            <category><![CDATA[angular14]]></category>
            <category><![CDATA[angular]]></category>
            <category><![CDATA[routing]]></category>
            <dc:creator><![CDATA[Andrew Scott]]></dc:creator>
            <pubDate>Tue, 25 Oct 2022 19:02:17 GMT</pubDate>
            <atom:updated>2022-10-25T19:02:17.897Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="Old world style map with a compass on top" src="https://cdn-images-1.medium.com/max/1024/0*CMRt_X2y06OTNl0U" /><figcaption><a href="https://www.pexels.com/photo/beige-analog-compass-697662/">Photo</a> by Ylanite Koppens</figcaption></figure><p>The Angular team has been busy making some meaningful updates to the Angular router that are available as of Angular v14.2. We’re pleased to share some recent improvements. Read on to learn more.</p><h3>New Router API for standalone</h3><p>We’ve <a href="https://angular.io/api/router/provideRouter">introduced</a> a way to use the Router without the need for RouterModule and improved tree shaking for reduced bundle sizes. First, let’s focus on the new router integration API.</p><p>Here’s how to use the router without a RouterModule. In the bootstrapApplication function, we provide the router configuration to the providers array using the provideRouter function. The function accepts an array of application routes. Here’s an example:</p><pre>// Bootstrap the main application component with routing capabilities</pre><pre>const appRoutes: Routes = […];<br>bootstrapApplication(AppComponent, {<br>  providers: [<br>    provideRouter(appRoutes),<br>  ]<br>});</pre><p>Compare this to the existing way to set up the Router in an application that required a module:</p><pre>const appRoutes: Routes = […];</pre><pre>bootstrapApplication(AppComponent, {<br>  providers: [<br>    importProvidersFrom(RouterModule.forRoot(appRoutes)),<br>  ]<br>});</pre><h3>Tree shaking</h3><p>The new provideRouter API allows developers to tree-shake major pieces of the router API. This is the first time we’ve had the ability to enable this level of tree-shaking in the Angular router. Now teams can enjoy smaller bundle sizes.</p><p>The design of the RouteModule API prevents bundlers from being able to remove unused Router features. With the RouteModule API the following features are included in the production bundle even if not enabled:</p><ul><li>HashLocationStrategy — generally used for legacy routing via updating the fragment instead of the path</li><li>preloading, scrolling, and logic for blocking app loading until navigation finishes for SSR.</li></ul><p>The new provideRouter API changes this behavior. The features in the list above are no longer included in the production bundle if they are not enabled.</p><p>In our testing with the new API, we found that removing these unused features from the bundle resulted in an 11% reduction in the size of the router code in the application bundle when no features are enabled.</p><p>Here’s an example of opting in to preloading using the withPreloading function :</p><pre>const appRoutes: Routes = [];</pre><pre>bootstrapApplication(AppComponent, {<br>  providers: [<br>    provideRouter(appRoutes, withPreloading(PreloadAllModules))<br>  ]<br>});</pre><h3>Now supporting functional router guards</h3><blockquote>Router guards require too much boilerplate…<br>- most Angular developers at some point, probably</blockquote><p>We’ve received feedback from multiple developers that amounts to developers wanting less boilerplate and more productivity. Let’s cover some of the exciting new changes that move us closer to this goal.</p><p>Functional router guards with inject() are lightweight, ergonomic, and more composable than class-based guards.</p><p>Here’s an example of functional guard that takes a component as a parameter and returns whether or not a route can be deactivated based on the component’s hasUnsavedChanges property:</p><pre>const route = {<br>  path: ‘edit’,<br>  component: EditCmp,<br>  canDeactivate: [<br>    (component: EditCmp) =&gt; !component.hasUnsavedChanges<br>  ]<br>};</pre><p>Additionally, these functional guards can still inject dependencies with inject from @angular/core :</p><pre>const route = {<br>  path: ‘admin’,<br>  canActivate: [() =&gt; inject(LoginService).isLoggedIn()]<br>};</pre><p>The Router APIs previously required guards and resolvers to be present in Angular’s dependency injection. This resulted in unnecessary boilerplate code. Let’s consider two examples.</p><p>In the first example, here’s the code required to provide a guard using an <a href="https://angular.io/api/core/InjectionToken">InjectionToken</a>:</p><pre>const UNSAVED_CHANGES_GUARD = new InjectionToken&lt;any&gt;(&#39;my_guard&#39;, {<br>  providedIn: &#39;root&#39;,<br>  factory: () =&gt; (component: EditCmp) =&gt; !component.hasUnsavedChanges<br>});</pre><pre>const route = {<br>  path: &#39;edit&#39;,<br>  component: EditCmp,<br>  canDeactivate: [UNSAVED_CHANGES_GUARD]<br>};</pre><p>In this second example, here’s the code required for providing a guards as an injectable class:</p><pre><a href="http://twitter.com/Injectable">@Injectable</a>({providedIn: &#39;root&#39;})<br>export class LoggedInGuard implements CanActivate {<br>  constructor(private loginService: LoginService) {}</pre><pre>  canActivate() {<br>    return this.loginService.isLoggedIn();<br>  }<br>}</pre><pre>const route = {<br>  path: &#39;admin&#39;,<br>  canActivate: [LoggedInGuard]<br>};</pre><p>Notice that even when we want to write a simple guard that has no dependencies as in the first example, we still have to write either an InjectionToken or an Injectable class. With functional guards developers can create guards, even guards with dependencies, with much less boilerplate.</p><h3>Functional guards are composable</h3><p>Functional guards are also more composable. A common feature request for the Router is to provide the option for guards to execute sequentially rather than all at once. Without this option in the Router implementing this type of behavior in an application would be difficult. This is smoother to implement with functional guards. There’s even an <a href="https://github.com/angular/angular/blob/8546b17adec01de69bf314a959ef2d12f6638eb9/packages/router/test/integration.spec.ts#L5157-L5194">example test</a> in the router code to demonstrate this behavior.</p><p>Let’s review the code to get a better understanding of how this works:</p><pre>function runSerially(guards: CanActivateFn[]|<br>                             CanActivateChildFn[]): <br>                             CanActivateFn|CanActivateChildFn {<br>  return (<br>          route: ActivatedRouteSnapshot, <br>          state: RouterStateSnapshot) =&gt; {<br>            const injector = inject(EnvironmentInjector);<br>            const observables = guards.map(guard =&gt; {<br>              const guardResult = injector.runInContext(<br>                () =&gt; guard(route, state));<br>              return wrapIntoObservable(guardResult).pipe(first());<br>            });</pre><pre>            return concat(…observables).pipe(<br>                     takeWhile(v =&gt; v === true), last());<br>            };<br>}</pre><p>With functional guards, you can create factory-like functions that accept a configuration and return a guard or resolver function. With this pattern, we now also have <a href="https://github.com/angular/angular/issues/42171">configurable guards and resolvers</a>, another common feature request. The runSerially function is a factory-like function that accepts a list of guard functions and returns a new guard.</p><pre>function runSerially(guards: CanActivateFn[]|CanActivateChildFn[]): CanActivateFn|CanActivateChildFn</pre><p>First, we obtain a reference to the current injector with:</p><pre>const injector = inject(EnvironmentInjector);</pre><p>When functional guards want to use dependency injection (DI), they must call inject synchronously. Each guard may execute asynchronously and may inject dependencies of their own. We need the reference to the current injector immediately so we can keep running each of them inside the same injection context:</p><pre>const guardResult = injector.runInContext(() =&gt; guard(route, state));</pre><p>This is also <a href="https://github.com/angular/angular/blob/8546b17adec01de69bf314a959ef2d12f6638eb9/packages/router/src/operators/check_guards.ts#L145">exactly</a> how the router enables the function guards feature.</p><p>Because guards can return an Observable, a Promise, or a synchronous result, we use our wrapIntoObservable helper to transform all results to an Observable and take only the first emitted value:</p><pre>return wrapIntoObservable(guardResult).pipe(first());</pre><p>We take all of these updated guard functions, execute them one after another (with concat) and only subscribe to the result while each of them indicate the route can be activated (i.e, returning true):</p><pre>concat(…observables).pipe(takeWhile(v =&gt; v === true), last());</pre><p>That’s it. We enabled running functional guards sequentially with less than 10 lines of code. We can now call it in our `Route` config like this:</p><pre>{<br>  path: ‘hello-world’,<br>  component: HelloWorld,<br>  canActivate: [runSerially(guard1, guard2, guard3)]<br>}</pre><h3>More to Come</h3><p>All of these changes are available as of Angular v14.2. We hope that you enjoy these updates to the router and we would love to hear what you think. You can find us online.</p><p>Thank you for continuing to be a part of the Angular community.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5d69ec4c032" width="1" height="1" alt=""><hr><p><a href="https://blog.angular.dev/advancements-in-the-angular-router-5d69ec4c032">Advancements in the Angular Router</a> was originally published in <a href="https://blog.angular.dev">Angular Blog</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>