PageSourceSearch

https://riegler.fr/assets/2023-09-30-lazy-loading-mockable-BrF7JmGt.js

js riegler.fr collected 2026-10-03 20:41:16 UTC 39,557 bytes, 219 lines download raw bytes

1const n=`---
2title: "Lazy loading your services in Angular with tests in mind"
3excerpt: "We should be able to mock what we lazy-load"
4coverImage: "src/content/posts/2023-09-30-lazy-loading-mockable/injector.jpg"
5date: "2023-09-30T00:00:00.000Z"
6---
7
8<p>Bundle size is a critical subject when building a SPA. The bigger it is, the longer it will take to start the app.
9Framework and developers have been relying more and more on lazy-loading to delay loading of non necessary code. The most common ones are lazy-loaded routes.</p>
10<p>When it comes to services, we can lazy load it by using a dynamic <code>import()</code>.
11Let&#39;s take as an example a situation where a page displays a heavy WebGL animation that we want to lazy-load.</p>
12<p>We suppose that <code>HomeAnimation</code> is a Angular service with <code>Injectable({providedIn: &#39;root&#39;})</code>.</p>
13<h4 id="homecomponentts"><strong><code>home.component.ts</code></strong></h4>
14<pre class="language-typescript"><code class="language-typescript"><span class="token keyword">class</span> <span class="token class-name">HomeComponent</span> <span class="token punctuation">{</span>
15  injector <span class="token operator">=</span> <span class="token function">inject</span><span class="token punctuation">(</span>Injector<span class="token punctuation">)</span><span class="token punctuation">;</span>
16  <span class="token operator">...</span>
17  <span class="token keyword">private</span> <span class="token keyword">async</span> <span class="token function">loadHomeAnimation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
18    <span class="token keyword">const</span> HomeAnimation <span class="token operator">=</span> <span class="token keyword">import</span><span class="token punctuation">(</span><span class="token string">'./services/home-animation.service'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token operator">=></span> c<span class="token punctuation">.</span>HomeAnimation<span class="token punctuation">)</span><span class="token punctuation">,</span>
19    <span class="token keyword">this</span><span class="token punctuation">.</span>injector<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>HomeAnimation<span class="token punctuation">)</span><span class="token punctuation">;</span>
20    <span class="token keyword">this</span><span class="token punctuation">.</span>homeAnimation<span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span>
21  <span class="token punctuation">}</span>
22<span class="token punctuation">}</span></code></pre><p>This is really straightforward but it has a major downside : This doesn&#39;t allow us to use a mock service in unit tests.</p>
23<h2 id="leverage-dependency-injection">Leverage dependency injection</h2>
24<p>DI is a powerful tool, we will leverage it here to create/share instances and to help on testing.</p>
25<p>First let&#39;s start with this helper function.</p>
26<pre class="language-ts"><code class="language-ts"><span class="token keyword">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token generic-function"><span class="token function">injectAsync</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>
27  injector<span class="token operator">:</span> Injector<span class="token punctuation">,</span>
28  <span class="token function-variable function">providerLoader</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span>ProviderToken<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">>></span><span class="token punctuation">,</span>
29<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span> <span class="token punctuation">{</span> 
30  <span class="token keyword">const</span> injectImpl <span class="token operator">=</span> injector<span class="token punctuation">.</span><span class="token function">
30get</span><span class="token punctuation">(</span>InjectAsyncImpl<span class="token punctuation">)</span><span class="token punctuation">;</span>
31  <span class="token keyword">return</span> injectImpl<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>providerLoader<span class="token punctuation">)</span><span class="token punctuation">;</span>
32<span class="token punctuation">}</span></code></pre><p>The injector will do 2 things here :</p>
33<ul>
34<li>Retrieve a an instance of <code>InjectAsyncImpl</code>, a class providing an implementation of lazy loading</li>
35<li>Create the instance of the service we just loaded (the class must be available to the injector, so you&#39;ll have to add <code>@Injectable({providedIn: &#39;root&#39;})</code> to the class you want to lazy load)</li>
36</ul>
37<p>The <code>InjectAsyncImpl</code> class can very simple:</p>
38<h4 id="inject-asyncts"><strong><code>inject-async.ts</code></strong></h4>
39<pre class="language-ts"><code class="language-ts"><span class="token decorator"><span class="token at operator">@</span><span class="token function">Injectable</span></span><span class="token punctuation">(</span><span class="token punctuation">{</span>providedIn<span class="token operator">:</span> <span class="token string">'root'</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
40<span class="token keyword">class</span> <span class="token class-name">InjectAsyncImpl<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span></span> <span class="token punctuation">{</span>
41  <span class="token keyword">async</span> <span class="token function">get</span><span class="token punctuation">(</span>injector<span class="token operator">:</span> Injector<span class="token punctuation">,</span> <span class="token function-variable function">providerLoader</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span>ProviderToken<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">>></span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span> <span class="token punctuation">{</span>
42    <span class="token keyword">const</span> type <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">providerLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
43
44    <span class="token keyword">return</span> injector<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span>
45  <span class="token punctuation">}</span>
46<span class="token punctuation">}</span></code></pre><p>With this implementation, we can now override the provider for <code>InjectAsyncImpl</code> in our unit test to return a mock instance of our lazy-loaded service.</p>
47<h4 id="my-testspects"><strong><code>my-test.spec.ts</code></strong></h4>
48<pre class="language-ts"><code class="language-ts">TestBed<span class="token punctuation">.</span><span class="token function">configureTestingModule</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
49  providers<span class="token operator">:</span> <span class="token punctuation">[</span>
50    <span class="token punctuation">{</span> provide<span class="token operator">:</span> InjectAsyncImpl<span class="token punctuation">,</span> useValue<span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token function">get</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token builtin">Promise</span><span class="token punctuation">.</span><span class="token function">resolve</span><span class="token punctuation">(</span>MyMockedService<span class="token punctuation">)</span> <span class="token punctuation">}</span><span class="token punctuation">}</span>
51  <span class="token punctuation">]</span>
52<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><p>Our current implementation works but the DX here is far from great. It&#39;s only easy to mock one lazy-loaded service and we&#39;d have to create a different class/object for every mocked service. So let&#39;s refactor that to improve it.</p>
53<h2 id="improving-dx">Improving DX</h2>
54<p>To simplify the mocking of our lazy-loaded services, we&#39;ll add support for overrides to our implementation.</p>
55<h4 id="inject-asyncts-1"><strong><code>inject-async.ts</code></strong></h4>
56<pre class="language-ts"><code class="language-ts"><span class="token decorator"><span class="token at operator">@</span><span class="token function">Injectable</span></span><span class="token punctuation">(</span><span class="token punctuation">{</span> providedIn<span class="token operator">:</span> <span class="token string">'root'</span> <span class="token punctuation">}</span><span class="token punctuation">)</span>
57<span class="token keyword">class</span> <span class="token class-name">InjectAsyncImpl<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span></span> <span class="token punctuation">{</span>
58  <span class="token keyword">private</span> overrides <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WeakMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// no need to cleanup</span>
59  <span class="token generic-function"><span class="token function">
59override</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>type<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span><span class="token punctuation">,</span> mock<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token builtin">unknown</span><span class="token operator">></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
60    <span class="token keyword">this</span><span class="token punctuation">.</span>overrides<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>type<span class="token punctuation">,</span> mock<span class="token punctuation">)</span><span class="token punctuation">;</span>
61  <span class="token punctuation">}</span>
62
63  <span class="token keyword">async</span> <span class="token function">get</span><span class="token punctuation">(</span>injector<span class="token operator">:</span> Injector<span class="token punctuation">,</span> <span class="token function-variable function">providerLoader</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span>ProviderToken<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">>></span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span> <span class="token punctuation">{</span>
64    <span class="token keyword">const</span> type <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">providerLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
65
66    <span class="token comment">// Check if we have overrides, O(1), low overhead</span>
67    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>overrides<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
68      <span class="token keyword">const</span> module <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>overrides<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">;</span>
69      <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name"><span class="token keyword">module</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
70    <span class="token punctuation">}</span>
71  <span class="token punctuation">}</span>
72<span class="token punctuation">}</span></code></pre><p>These overrides will be set by a helper function</p>
73<pre class="language-ts"><code class="language-ts"><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token generic-function"><span class="token function">mockAsyncProvider</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>type<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span><span class="token punctuation">,</span> mock<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token builtin">unknown</span><span class="token operator">></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
74  <span class="token keyword">return</span> <span class="token punctuation">[</span>
75    <span class="token punctuation">{</span>
76      provide<span class="token operator">:</span> <span class="token constant">ENVIRONMENT_INITIALIZER</span><span class="token punctuation">,</span>
77      multi<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
78      <span class="token function-variable function">useValue</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
79        <span class="token function">inject</span><span class="token punctuation">(</span>InjectAsyncImpl<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">override</span><span class="token punctuation">(</span>type<span class="token punctuation">,</span> mock<span class="token punctuation">)</span><span class="token punctuation">;</span>
80      <span class="token punctuation">}</span><span class="token punctuation">,</span>
81    <span class="token punctuation">}</span><span class="token punctuation">,</span>
82  <span class="token punctuation">]</span><span class="token punctuation">;</span>
83<span class="token punctuation">}</span></code></pre><p><code>ENVIRONMENT_INITIALIZER</code> is a multi-provider token for initialization functions that will run upon construction of an environment injector. So the <code>override</code> method will be called on setup for every mocked service.</p>
84<p>Now we have a nice &amp; clean testing setup!</p>
85<h4 id="my-testspects-1"><strong><code>my-test.spec.ts</code></strong></h4>
86<pre class="language-ts"><code class="language-ts"> <span class="token operator">*</span> TestBed<span class="token punctuation">.</span><span class="token function">configureTestingModule</span><span class="token punctuation">(</span><span class="token punctuation">{</span>
87 <span class="token operator">*</span>     providers<span class="token operator">:</span> <span class="token punctuation">[</span>
88 <span class="token operator">*</span>       <span class="token function">mockAsyncProvider</span><span class="token punctuation">(</span>MyFooService<span class="token punctuation">,</span> MyFakeService<span class="token punctuation">)</span>
89 <span class="token operator">*</span>       <span class="token function">mockAsyncProvider</span><span class="token punctuation">(</span>MyBarService<span class="token punctuation">,</span> MyBarService<span class="token punctuation">)</span>
90 <span class="token operator">*</span>   <span class="token punctuation">]</span>
91 <span class="token operator">*</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre><h2 id="improving-angular-integration">Improving Angular integration</h2>
92<p>When providing a service in root, the service will be created by the root injector.
93This has a major downside of never firing <code>DestroyRef.onDestroy</code>.</p>
94<p>From the <a href="https://angular.io/api/core/DestroyRef">official docs</a>:</p>
95<blockquote>
96<p>If DestroyRef is injected in a component or directive, the callbacks run when that component or directive is destroyed. Otherwise the callbacks run when a corresponding injector is destroyed.</p>
97</blockquote>
98<p>So if we want a lazy-loaded service that gets destroyed with the component that created it, we&#39;ll need to change our implementation.</p>
99<p>Our constraints are :</p>
100<ul>
101<li>The service cannot be <code>providedIn: &#39;root&#39;</code> (we want it to be destroyed with the <code>NodeInjector</code>)</li>
102<li>The service cannot be provided on the component, this would break the lazy loading</li>
103<li>We can&#39;t add a class to an injector, we&#39;ll need to create a new one.</li>
104</ul>
105<p>With this in mind, we&#39;ll add following</p>
106<pre class="language-ts"><code class="language-ts"><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>injector <span class="token keyword">instanceof</span> <span class="token class-name">EnvironmentInjector</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
107  <span class="token comment">// We're passing a node injector to the function</span>
108
109  <span class="token comment">// This is the DestroyRef of the component</span>
110  <span class="token keyword">const</span> destroyRef <span class="token operator">=</span> injector<span class="token punctuation">.</span><span class="token function">
110get</span><span class="token punctuation">(</span>DestroyRef<span class="token punctuation">)</span><span class="token punctuation">;</span>
111
112  <span class="token comment">// This is the parent injector of the environmentInjector we're creating</span>
113  <span class="token keyword">const</span> environmentInjector <span class="token operator">=</span> injector<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>EnvironmentInjector<span class="token punctuation">)</span><span class="token punctuation">;</span>
114
115  <span class="token comment">// Creating an environment injector to destroy it afterwards</span>
116  <span class="token keyword">const</span> newInjector <span class="token operator">=</span> <span class="token function">createEnvironmentInjector</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token keyword">type</span> <span class="token class-name"><span class="token keyword">as</span></span> Provider<span class="token punctuation">]</span><span class="token punctuation">,</span> environmentInjector<span class="token punctuation">)</span><span class="token punctuation">;</span>
117
118  <span class="token comment">// Destroy the injector to trigger DestroyRef.onDestroy on our service</span>
119  destroyRef<span class="token punctuation">.</span><span class="token function">onDestroy</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
120    newInjector<span class="token punctuation">.</span><span class="token function">destroy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
121  <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
122
123  <span class="token comment">// We want to create the new instance of our service with our new injector </span>
124  injector <span class="token operator">=</span> newInjector<span class="token punctuation">;</span>
125<span class="token punctuation">}</span>
126<span class="token keyword">return</span> injector<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>module<span class="token punctuation">)</span><span class="token operator">!</span><span class="token punctuation">;</span></code></pre><p>If we go back to our initial example, this how we would use our new helper method to lazy-load the HomeAnimation service.</p>
127<pre class="language-ts"><code class="language-ts"><span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">HomeComponent</span> <span class="token punctuation">{</span>
128  <span class="token keyword">private</span> <span class="token keyword">readonly</span> injector <span class="token operator">=</span> <span class="token function">inject</span><span class="token punctuation">(</span>Injector<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// The Node injector</span>
129
130  <span class="token operator">...</span>
131
132  <span class="token keyword">private</span> <span class="token keyword">async</span> <span class="token function">loadHomeAnimation</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
133    <span class="token keyword">this</span><span class="token punctuation">.</span>homeAnimation <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">injectAsync</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>injector<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span>
134      <span class="token keyword">
134import</span><span class="token punctuation">(</span><span class="token string">'./services/home-animation.service'</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">then</span><span class="token punctuation">(</span><span class="token punctuation">(</span>c<span class="token punctuation">)</span> <span class="token operator">=></span> c<span class="token punctuation">.</span>HomeAnimation<span class="token punctuation">)</span><span class="token punctuation">,</span>
135    <span class="token punctuation">)</span><span class="token punctuation">;</span>
136
137    <span class="token keyword">this</span><span class="token punctuation">.</span>homeAnimation<span class="token punctuation">.</span><span class="token function">init</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>element<span class="token punctuation">)</span><span class="token punctuation">;</span>
138  <span class="token punctuation">}</span>
139<span class="token punctuation">}</span></code></pre><p>It is important here that the injector is a <code>NodeInjector</code> and not the <code>EnvironmentInjector</code>.
140The former will be destroyed with the component while the latter will survive the component.</p>
141<h2 id="final-implementation">Final implementation</h2>
142<p>Here is our final implementation of the <code>inject-async.ts</code> helper functions.</p>
143<pre class="language-ts"><code class="language-ts"><span class="token keyword">import</span> <span class="token punctuation">{</span>createEnvironmentInjector<span class="token punctuation">,</span> DestroyRef<span class="token punctuation">,</span> <span class="token constant">ENVIRONMENT_INITIALIZER</span><span class="token punctuation">,</span> EnvironmentInjector<span class="token punctuation">,</span> inject<span class="token punctuation">,</span> Injectable<span class="token punctuation">,</span> Injector<span class="token punctuation">,</span> Provider<span class="token punctuation">,</span> ProviderToken<span class="token punctuation">,</span> Type<span class="token punctuation">,</span><span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'@angular/core'</span><span class="token punctuation">;</span>
144
145<span class="token comment">/**
146 * inject a service asynchronously
147 *
148 * @param: injector. If the injector is a NodeInjector the loaded module will be destroyed alongside its injector
149 */</span>
150<span class="token keyword">export</span> <span class="token keyword">async</span> <span class="token keyword">function</span> <span class="token generic-function"><span class="token function">injectAsync</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>
151  injector<span class="token operator">:</span> Injector<span class="token punctuation">,</span>
152  <span class="token function-variable function">providerLoader</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span>ProviderToken<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">>></span><span class="token punctuation">,</span>
153<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span> <span class="token punctuation">{</span>
154  <span class="token keyword">const</span> injectImpl <span class="token operator">=</span> injector<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>InjectAsyncImpl<span class="token punctuation">)</span><span class="token punctuation">;</span>
155  <span class="token keyword">return</span> injectImpl<span class="token punctuation">.</span><span class="token function">
155get</span><span class="token punctuation">(</span>injector<span class="token punctuation">,</span> providerLoader<span class="token punctuation">)</span><span class="token punctuation">;</span>
156<span class="token punctuation">}</span>
157
158<span class="token decorator"><span class="token at operator">@</span><span class="token function">Injectable</span></span><span class="token punctuation">(</span><span class="token punctuation">{</span>providedIn<span class="token operator">:</span> <span class="token string">'root'</span><span class="token punctuation">}</span><span class="token punctuation">)</span>
159<span class="token keyword">class</span> <span class="token class-name">InjectAsyncImpl<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span></span> <span class="token punctuation">{</span>
160  <span class="token keyword">private</span> overrides <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WeakMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// no need to cleanup</span>
161  <span class="token generic-function"><span class="token function">override</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>type<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span><span class="token punctuation">,</span> mock<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token builtin">unknown</span><span class="token operator">></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
162    <span class="token keyword">this</span><span class="token punctuation">.</span>overrides<span class="token punctuation">.</span><span class="token function">set</span><span class="token punctuation">(</span>type<span class="token punctuation">,</span> mock<span class="token punctuation">)</span><span class="token punctuation">;</span>
163  <span class="token punctuation">}</span>
164
165  <span class="token keyword">async</span> <span class="token function">get</span><span class="token punctuation">(</span>injector<span class="token operator">:</span> Injector<span class="token punctuation">,</span> <span class="token function-variable function">providerLoader</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span>ProviderToken<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">>></span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token builtin">Promise</span><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span> <span class="token punctuation">{</span>
166    <span class="token keyword">const</span> type <span class="token operator">=</span> <span class="token keyword">await</span> <span class="token function">providerLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
167
168    <span class="token comment">// Check if we have overrides, O(1), low overhead</span>
169    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>overrides<span class="token punctuation">.</span><span class="token function">has</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
170      <span class="token keyword">const</span> module <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>overrides<span class="token punctuation">.</span><span class="token function">
170get</span><span class="token punctuation">(</span>type<span class="token punctuation">)</span><span class="token punctuation">;</span>
171      <span class="token keyword">return</span> <span class="token keyword">new</span> <span class="token class-name"><span class="token keyword">module</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
172    <span class="token punctuation">}</span>
173
174    <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token punctuation">(</span>injector <span class="token keyword">instanceof</span> <span class="token class-name">EnvironmentInjector</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
175      <span class="token comment">// We're passing a node injector to the function</span>
176
177      <span class="token comment">// This is the DestroyRef of the component</span>
178      <span class="token keyword">const</span> destroyRef <span class="token operator">=</span> injector<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>DestroyRef<span class="token punctuation">)</span><span class="token punctuation">;</span>
179
180      <span class="token comment">// This is the parent injector of the environmentInjector we're creating</span>
181      <span class="token keyword">const</span> environmentInjector <span class="token operator">=</span> injector<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>EnvironmentInjector<span class="token punctuation">)</span><span class="token punctuation">;</span>
182
183      <span class="token comment">// Creating an environment injector to destroy it afterwards</span>
184      <span class="token keyword">const</span> newInjector <span class="token operator">=</span> <span class="token function">createEnvironmentInjector</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token keyword">type</span> <span class="token class-name"><span class="token keyword">as</span></span> Provider<span class="token punctuation">]</span><span class="token punctuation">,</span> environmentInjector<span class="token punctuation">)</span><span class="token punctuation">;</span>
185
186      <span class="token comment">// Destroy the injector to trigger DestroyRef.onDestroy on our service</span>
187      destroyRef<span class="token punctuation">.</span><span class="token function">onDestroy</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
188        newInjector<span class="token punctuation">.</span><span class="token function">destroy</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
189      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
190
191      <span class="token comment">// We want to create the new instance of our service with our new injector </span>
192      injector <span class="token operator">=</span> newInjector<span class="token punctuation">;</span>
193    <span class="token punctuation">}</span>
194
195    <span class="token keyword">return</span> injector<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span>module<span class="token punctuation">)</span><span class="token operator">!</span><span class="token punctuation">;</span>
196  <span class="token punctuation">}</span>
197<span class="token punctuation">}</span>
198
199<span class="token comment">/**
200 * Helper function to mock the lazy-loaded module in \`injectAsync\`
201 *
202 * @usage
203 * TestBed.configureTestingModule({
204 *     providers: [
205 *     mockAsyncProvider(SandboxService, fakeSandboxService)
206 *   ]
207 * });
208 */</span>
209<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token generic-function"><span class="token function">mockAsyncProvider</span><span class="token generic class-name"><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span></span></span><span class="token punctuation">(</span>type<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">></span><span class="token punctuation">,</span> mock<span class="token operator">:</span> Type<span class="token operator">&lt;</span><span class="token builtin">unknown</span><span class="token operator">></span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
210  <span class="token keyword">return</span> <span class="token punctuation">[</span>
211    <span class="token punctuation">{</span>
212      provide<span class="token operator">:</span> <span class="token constant">ENVIRONMENT_INITIALIZER</span><span class="token punctuation">,</span>
213      multi<span class="token operator">:</span> <span class="token boolean">true</span><span class="token punctuation">,</span>
214      <span class="token function-variable function">useValue</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span>
215        <span class="token function">inject</span><span class="token punctuation">(</span>InjectAsyncImpl<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">override</span><span class="token punctuation">(</span>type<span class="token punctuation">,</span> mock<span class="token punctuation">)</span><span class="token punctuation">;</span>
216      <span class="token punctuation">}</span><span class="token punctuation">,</span>
217    <span class="token punctuation">}</span><span class="token punctuation">,</span>
218  <span class="token punctuation">]</span><span class="token punctuation">;</span>
219<span class="token punctuation">}</span></code></pre>`;export{n as default};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.