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'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: 'root'})</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'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'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"><</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"><</span>ProviderToken<span class="token operator"><</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"><</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'll have to add <code>@Injectable({providedIn: 'root'})</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"><</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"><</span>ProviderToken<span class="token operator"><</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"><</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's only easy to mock one lazy-loaded service and we'd have to create a different class/object for every mocked service. So let'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'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"><</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"><</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"><</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"><</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"><</span>ProviderToken<span class="token operator"><</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"><</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"><</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"><</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"><</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 & 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'll need to change our implementation.</p> 99<p>Our constraints are :</p> 100<ul> 101<li>The service cannot be <code>providedIn: 'root'</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't add a class to an injector, we'll need to create a new one.</li> 104</ul> 105<p>With this in mind, we'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"><</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"><</span>ProviderToken<span class="token operator"><</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"><</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"><</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"><</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"><</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"><</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"><</span>ProviderToken<span class="token operator"><</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"><</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"><</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"><</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"><</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.