{"id":24334,"date":"2024-01-25T08:07:29","date_gmt":"2024-01-25T07:07:29","guid":{"rendered":"https:\/\/www.angulararchitects.io\/blog\/testing-angulars-latest-features\/"},"modified":"2024-01-26T18:29:59","modified_gmt":"2024-01-26T17:29:59","slug":"testing-angulars-latest-features","status":"publish","type":"post","link":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/","title":{"rendered":"Testing Angular&#8217;s Latest Features"},"content":{"rendered":"<h2>1. Standalone &amp; Mocking<\/h2>\n<p>If you prefer the kind of tests where you minimize your mock as much as possible, you will be quite happy with Standalone Components. Forgotten are the days when we had to carefully select only those dependencies from an <code>NgModule<\/code> that belong to the component under test and move it over to the <code>TestingModule<\/code>.<\/p>\n<p>The Standalone Component has everything in it. Just add it to the <code>imports<\/code> property of the <code>TestingModule<\/code>. All its &quot;visual elements&quot;, like sub components, directives, and pipes, and their dependencies are now part of the test as well. In terms of code coverage, that's quite huge.<\/p>\n<p>When we write a test, we check what services our component requires. Typical candidates are <code>HttpClient<\/code>, <code>ActivatedRoute<\/code>. We need to mock them. That's doable.<\/p>\n<p>Unfortunately, the components' dependencies also require services, which - some of them - we also have to provide.<\/p>\n<p>Let's look at the following component <code>RequestInfo<\/code>, we want to test:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/Slide1.jpeg\"><\/p>\n<p>A huge amount of services derive from RequestInfoHolidayCard. With NgRx, we have quite an additional heavy dependency.<\/p>\n<p>Looking at the necessary setup of the <code>TestingModule<\/code>, there is quite a lot to consider:<\/p>\n<pre><code class=\"language-typescript\">const fixture = TestBed.configureTestingModule({\n  imports: [RequestInfoComponent],\n  providers: [\n    provideNoopAnimations(),\n    {\n      provide: HttpClient,\n      useValue: {\n        get: (url: string) =&gt; {\n          if (url === &quot;\/holiday&quot;) {\n            return of([createHoliday()]);\n          }\n          return of([true]).pipe(delay(125));\n        },\n      },\n    },\n    {\n      provide: ActivatedRoute,\n      useValue: {\n        paramMap: of({ get: () =&gt; 1 }),\n      },\n    },\n    provideStore({}),\n    provideState(holidaysFeature),\n    provideEffects([HolidaysEffects]),\n    {\n      provide: Configuration,\n      useValue: { baseUrl: &quot;https:\/\/somewhere.com&quot; },\n    },\n  ],\n}).createComponent(RequestInfoComponent);<\/code><\/pre>\n<p>What we want to achieve is that we mock only the <code>RequestInfoHolidayCard<\/code>. That would free us from quite a lot of service dependencies:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/Slide2.jpeg\"><\/p>\n<p>To mock a component, we do it the manual way. There are third-party libraries available, like ng-mocks, which provide functions to automate that.<\/p>\n<p>We add the code of the mocked Component directly into the test file<\/p>\n<pre><code class=\"language-typescript\">@Component({\n  selector: &quot;app-request-info-holiday-card&quot;,\n  template: ``,\n  standalone: true,\n})\nclass MockedRequestInfoHolidayCard {}<\/code><\/pre>\n<p>The <code>MockedRequestInfoHolidayCard<\/code> is a simple component without any dependencies. What it has in common with the original is the selector. So when Angular sees the tag <code>&lt;app-request-info-holiday-card&gt;<\/code>, it would use the mocked version.<\/p>\n<p>The next step is to import the mock into the <code>TestingModule<\/code>. With all its dependencies gone, the <code>TestingModule<\/code> setup slims down quite a bit:<\/p>\n<pre><code class=\"language-typescript\">const fixture = TestBed.configureTestingModule({\n  imports: [RequestInfoComponent, MockedRequestInfoHolidayCard],\n  providers: [\n    provideNoopAnimations(),\n    {\n      provide: HttpClient,\n      useValue: {\n        get: (url: string) =&gt; of([true]).pipe(delay(125)),\n      },\n    },\n  ],\n}).createComponent(RequestInfoComponent);<\/code><\/pre>\n<p>Unfortunately, that does not work. The test fails because <code>ActivatedRoute<\/code> (dependency of <code>RequestInfoHolidayCard<\/code>) is not available.<\/p>\n<p>The reason should be clear. <code>RequestInfoHolidayCard<\/code> is not part of the imports property of some NgModule but directly of the <code>RequestInfoComponent<\/code>. Although the mocked version is now part of the <code>TestingModule<\/code>, the imports coming from <code>RequestInfoComponent<\/code> internally override it.<\/p>\n<p>Our only chance is to access the imports property of the component itself. Luckily, there is a <code>TestBed.overrideComponent<\/code>. A method, made for that use case. After replacing the original with the mock, we configure the <code>TestingModule<\/code> and proceed with the actual test.<\/p>\n<pre><code class=\"language-typescript\">TestBed.overrideComponent(RequestInfoComponent, {\n  remove: { imports: [RequestInfoComponentHolidayCard] },\n  add: { imports: [MockedRequestInfoHolidayCard] },\n});\n\nconst fixture = TestBed.configureTestingModule({\n  imports: [RequestInfoComponent],\n  providers: [\n    provideNoopAnimations(),\n    {\n      provide: HttpClient,\n      useValue: {\n        get: (url: string) =&gt; of([true]).pipe(delay(125)),\n      },\n    },\n  ],\n}).createComponent(RequestInfoComponent);<\/code><\/pre>\n<p>Et voila, that's much better!<\/p>\n<p>A <code>set<\/code> method would override the complete <code>imports<\/code>, <code>providers<\/code>, etc. properties.<\/p>\n<p>Please note that I highly recommend that you use ng-mocks. It is way more comfortable to mock Components, Pipes, and Directives with it.<\/p>\n<h2>2. <code>inject()<\/code><\/h2>\n<p>With the introduction of the <code>inject<\/code> function in Angular 14, it became quite clear that we ought to use it over the constructor. To give you some reasons:<\/p>\n<ul>\n<li>In contrast to the constructor, the <code>inject<\/code> function works without parameter decorators. Except for the parameter decorators, TC39 finished the standardization of decorators in general. All of them have to be final to make the full switch from experimental to standardized parameters. Thus, the DI via constructor causes potential risks for future breaking changes.<\/li>\n<li>The type of <code>inject<\/code> is also available during runtime. With the constructor, only the variable name survives the compilation. So, the TypeScript compiler has to add certain metadata where type information is part of the bundle.<\/li>\n<li>In terms of inheritance, it is also a little bit easier.<\/li>\n<li>Injection-Tokens are type-safe.<\/li>\n<li>Some functional-based approaches (like the NgRx Store) that don't have a constructor can only work with <code>inject<\/code>.<\/li>\n<\/ul>\n<p>At the time of this writing, things shifted a little bit. According to Alex Rickabaugh, property decorators are in Stage 1 regarding standardization. There are also some drawbacks with <code>inject<\/code>, especially with testing. That's why he recommends using whatever fits best and waiting for the results of TC39.<\/p>\n<p>There is a video where I show how to instantiate a component manually and not use the <code>TestBed<\/code>. The conclusion at the end is, though, to go with the <code>TestBed<\/code>. Manual instantiation with <code>inject<\/code> is simply not worth it.<\/p>\n<p>After a bit of context, let's get to it.<\/p>\n<p>First, whenever we are dealing with a Service\/<a href=\"mailto:code&gt;@Injectable&lt;\/code\">code>@Injectable<\/code<\/a>, we can call <code>TestBed.instantiate<\/code> from everywhere in our test. At the beginning, in the middle, or even at the end.<\/p>\n<p>We have the following service, which we want to test:<\/p>\n<pre><code class=\"language-typescript\">@Injectable({ providedIn: &quot;root&quot; })\nexport class AddressAsyncValidator {\n  #httpClient = inject(HttpClient);\n\n  validate(ac: AbstractControl&lt;string&gt;): Observable&lt;ValidationErrors | null&gt; {\n    return this.#httpClient\n      .get&lt;unknown[]&gt;(&quot;https:\/\/nominatim.openstreetmap.org\/search.php&quot;, {\n        params: new HttpParams().set(&quot;format&quot;, &quot;jsonv2&quot;).set(&quot;q&quot;, ac.value),\n      })\n      .pipe(\n        map((addresses) =&gt;\n          addresses.length &gt; 0 ? null : { address: &quot;invalid&quot; }\n        )\n      );\n  }\n}<\/code><\/pre>\n<p><code>AddressAsyncValidator<\/code>injects the <code>HttpClient<\/code>. So we have to mock that one. There is no need to import or create any component in our <code>TestingModule<\/code>. It is just &quot;logic testing&quot; - no UI:<\/p>\n<pre><code class=\"language-typescript\">describe(&quot;AddressAsyncValidator&quot;, () =&gt; {\n  it(&quot;should validate an invalid address&quot;, waitForAsync(async () =&gt; {\n    TestBed.configureTestingModule({\n      providers: [\n        {\n          provide: HttpClient,\n          useValue: { get: () =&gt; of([]).pipe(delay(0)) },\n        },\n      ],\n    });\n\n    const validator = TestBed.inject(AddressAsyncValidator);\n    const isValid = await lastValueFrom(\n      validator.validate({ value: &quot;Domgasse 5&quot; } as AbstractControl)\n    );\n    expect(isValid).toEqual({ address: &quot;invalid&quot; });\n  }));\n});<\/code><\/pre>\n<p>That test will run. Two remarks.<\/p>\n<p>First, if the <code>{providedIn: &#039;root&#039;}<\/code> is missing in <code>AddressAsyncValidator<\/code>(only <a href=\"mailto:code&gt;@Injectable&lt;\/code\">code>@Injectable<\/code<\/a> is available), we can very easily provide the service in our test.<\/p>\n<p>That would be:<\/p>\n<pre><code class=\"language-typescript\">@Injectable()\nexport class AddressAsyncValidator {\n  \/\/ ...\n}\n\ndescribe(&quot;AddressAsyncValidator&quot;, () =&gt; {\n  it(&quot;should validate an invalid address&quot;, waitForAsync(async () =&gt; {\n    TestBed.configureTestingModule({\n      providers: [\n        AddressAsyncValidator,\n        {\n          provide: HttpClient,\n          useValue: { get: () =&gt; of([]).pipe(delay(0)) },\n        },\n      ],\n    });\n\n    \/\/ rest of the test\n  }));\n});<\/code><\/pre>\n<p>Second, what you cannot do is to run <code>inject<\/code> inside the test. That will fail with the familiar error message: <strong>NG0203: inject() must be called from an injection context such as a constructor, a factory function, a field initializer, or a function used with <code>runInInjectionContext<\/code><\/strong>.<\/p>\n<pre><code class=\"language-typescript\">describe(&quot;AddressAsyncValidator&quot;, () =&gt; {\n  it(&quot;should validate an invalid address&quot;, waitForAsync(async () =&gt; {\n    TestBed.configureTestingModule({\n      providers: [\n        AddressAsyncValidator,\n        {\n          provide: HttpClient,\n          useValue: { get: () =&gt; of([]).pipe(delay(0)) },\n        },\n      ],\n    });\n\n    const validator = inject(AddressAsyncValidator); \/\/ not good\n  }));\n});<\/code><\/pre>\n<p>Now, would we ever want to do that? Well, whenever we have a function that runs in the injection context, we have no other chance. In the Angular framework, that could be an <code>HttpInterceptorFn<\/code>or one of the router guards, like <code>CanActivateFn<\/code>.<\/p>\n<p>Especially in the Angular community, we currently see quite a lot of experiments with patterns that are function-based. A good start might be <a href=\"https:\/\/github.com\/nxtensions\/nxtensions\"><a href=\"https:\/\/github.com\/nxtensions\/nxtensions\">https:\/\/github.com\/nxtensions\/nxtensions<\/a><\/a><\/p>\n<p>We will stick to native features, though and test a <code>CanActivateFn<\/code>:<\/p>\n<pre><code class=\"language-typescript\">export const apiCheckGuard: CanActivateFn = (route, state) =&gt; {\n  const httpClient = inject(HttpClient);\n\n  return httpClient.get(&quot;\/holiday&quot;).pipe(map(() =&gt; true));\n};<\/code><\/pre>\n<p>That is a simple function that verifies if a request to the url &quot;\/holiday&quot; succeeds, and it makes use of the <code>inject<\/code> function.<\/p>\n<p>What do we do if we want to test the function in isolation?<\/p>\n<p>A test could look like this:<\/p>\n<pre><code class=\"language-typescript\">it(&quot;should return true&quot;, waitForAsync(async () =&gt; {\n  TestBed.configureTestingModule({\n    providers: [\n      { provide: HttpClient, useValue: { get: () =&gt; of(true).pipe(delay(1)) } },\n    ],\n  });\n\n  expect(await lastValueFrom(apiCheckGuard())).toBe(true);\n}));<\/code><\/pre>\n<p>That will also not work. We get the NG0203 error again.<\/p>\n<p>There is a solution, though. For those cases only, we can make use <code>TestBed.runInInjectionContext<\/code>:<\/p>\n<pre><code class=\"language-typescript\">describe(&quot;Api Check Guard&quot;, () =&gt; {\n  it(&quot;should return true&quot;, waitForAsync(async () =&gt; {\n    TestBed.configureTestingModule({\n      providers: [\n        {\n          provide: HttpClient,\n          useValue: { get: () =&gt; of(true).pipe(delay(1)) },\n        },\n      ],\n    });\n\n    await TestBed.runInInjectionContext(async () =&gt; {\n      const value$ = apiCheckGuard();\n      expect(await lastValueFrom(value$)).toBe(true);\n    });\n  }));\n});<\/code><\/pre>\n<p>Please note that the call to <code>inject<\/code> needs to happen synchronously. In our guard, that's the case.<\/p>\n<p>Our examples give us the illusion that the injection context also exists for <code>await<\/code>. That's not the case, but it doesn't matter. As seen as the request is out, the <code>inject<\/code> already did its job.<\/p>\n<h2>3. Router Parameter Binding with <code>RouterTestingHarness<\/code><\/h2>\n<p>Writing a test where the routing plays a role and where we don't want to mock it completely was always hard. Especially because the official documentation about the <code>RouterTestingModule<\/code> or <code>provideLocationMock<\/code> was not the best.<\/p>\n<p>Some community projects, especially Spectacular from Lars Nielsen, have helped us in the meantime.<\/p>\n<p>In Angular 16.2, we could fetch router parameters via the @Input. With 17.1, we have Signal Inputs as an alternative to the input function.<\/p>\n<p>Still, the question persists: How do you test it without mocking too much?<\/p>\n<p>The answer has already been available since Angular 15.2. It introduced the <code>RouterTestingHarness<\/code>. It makes testing with minimal mocking a breeze.<\/p>\n<p>This is the component we want to test:<\/p>\n<pre><code class=\"language-typescript\">@Component({\n  selector: &quot;app-detail&quot;,\n  template: <code>&lt;p&gt;Current Id: {{ id() }}&lt;\/p&gt;<\/code>,\n  standalone: true,\n})\nexport class DetailComponent {\n  id = signal(0);\n\n  constructor() {\n    inject(ActivatedRoute)\n      .paramMap.pipe(takeUntilDestroyed())\n      .subscribe((paramMap) =&gt; this.id.set(Number(paramMap.get(&quot;id&quot;) || &quot;0&quot;)));\n  }\n}<\/code><\/pre>\n<p>The <code>RouterTestingHarness<\/code> replaces the common <code>TestBed::createComponent<\/code> pattern. It creates the component but wraps it inside a &quot;testing routing context&quot;.<\/p>\n<p>Harnesses are quite popular in Angular Material. They are testing helper classes that make testing very convenient by managing parts of asynchronous execution and triggering the change detection.<\/p>\n<p>There is one requirement, though. Running Harness commands are usually asynchronous. So we always end up with async\/await tests.<\/p>\n<p>For every routing, we require a configuration. It is the same here:<\/p>\n<pre><code class=\"language-typescript\">describe(&quot;Detail Component&quot;, () =&gt; {\n  it(&quot;should test verify the id is 5&quot;, waitForAsync(async () =&gt; {\n    TestBed.configureTestingModule({\n      providers: [\n        provideRouter([{ path: &quot;detail\/:id&quot;, component: DetailComponent }]),\n      ],\n    });\n  }));\n});<\/code><\/pre>\n<p>Next, we instantiate the <code>RouterTestingHarness<\/code> and use it to navigate to &quot;\/detail\/5&quot;.<\/p>\n<pre><code class=\"language-typescript\">const harness = await RouterTestingHarness.create(&quot;detail\/5&quot;);<\/code><\/pre>\n<p>Internally, our component's subscription to the route runs, and we should already see that the <code>id<\/code> in the template shows the value 5:<\/p>\n<pre><code class=\"language-typescript\">const p: HTMLParagraphElement = harness.fixture.debugElement.query(\n  By.css(&quot;p&quot;)\n).nativeElement;\n\nexpect(p.textContent).toBe(&quot;Current Id: 5&quot;);<\/code><\/pre>\n<p>We can now even continue our test. For example, we might want to stay at the same route but want to switch to a different <code>id<\/code>.<\/p>\n<p>No problem with the <code>RouterTestingHarness<\/code>. For the sake of completeness, here's the full code of the test:<\/p>\n<pre><code class=\"language-typescript\">describe(&quot;Detail Component&quot;, () =&gt; {\n  it(&quot;should test verify the id is 5&quot;, waitForAsync(async () =&gt; {\n    TestBed.configureTestingModule({\n      providers: [\n        provideRouter([{ path: &quot;detail\/:id&quot;, component: DetailComponent }]),\n      ],\n    });\n\n    const harness = await RouterTestingHarness.create(&quot;detail\/5&quot;);\n\n    const p: HTMLParagraphElement = harness.fixture.debugElement.query(\n      By.css(&quot;p&quot;)\n    ).nativeElement;\n\n    expect(p.textContent).toBe(&quot;Current Id: 5&quot;);\n\n    await harness.navigateByUrl(&quot;detail\/6&quot;);\n    expect(p.textContent).toBe(&quot;Current Id: 6&quot;);\n  }));\n});<\/code><\/pre>\n<p>Please note that we didn't have to trigger the change detection or do anything else when we switched to &quot;\/detail\/6&quot;. The Harness did everything for us internally. The only thing which we must not forget is to use the <code>await<\/code>.<\/p>\n<p>Testing with the <code>RouterTestingHarness<\/code> is much better than mocking the <code>ActivatedRouter<\/code>.<\/p>\n<p>Whenever you make functions outside of your control, like <code>ActivatedRouter<\/code>, you can never be sure that your mocking behaves exactly as the original.<\/p>\n<p>The original runs some asynchronous tasks; maybe it misses triggering the change detection or something else. Quite a lot of traps you might fall into.<\/p>\n<p>Better be safe and leave the internal functions to the internal functions \ud83d\ude00.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>1. Standalone &amp; Mocking If you prefer the kind of tests where you minimize your mock as much as possible, you will be quite happy with Standalone Components. Forgotten are the days when we had to carefully select only those dependencies from an NgModule that belong to the component under test and move it over [&hellip;]<\/p>\n","protected":false},"author":13,"featured_media":24331,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_price":"","_stock":"","_tribe_ticket_header":"","_tribe_default_ticket_provider":"","_ticket_start_date":"","_ticket_end_date":"","_tribe_ticket_show_description":"","_tribe_ticket_show_not_going":false,"_tribe_ticket_use_global_stock":"","_tribe_ticket_global_stock_level":"","_global_stock_mode":"","_global_stock_cap":"","_tribe_rsvp_for_event":"","_tribe_ticket_going_count":"","_tribe_ticket_not_going_count":"","_tribe_tickets_list":"[]","_tribe_ticket_has_attendee_info_fields":false,"footnotes":""},"categories":[18],"tags":[],"class_list":["post-24334","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v27.1.1 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Testing Angular&#039;s Latest Features - ANGULARarchitects<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Testing Angular&#039;s Latest Features - ANGULARarchitects\" \/>\n<meta property=\"og:description\" content=\"1. Standalone &amp; Mocking If you prefer the kind of tests where you minimize your mock as much as possible, you will be quite happy with Standalone Components. Forgotten are the days when we had to carefully select only those dependencies from an NgModule that belong to the component under test and move it over [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/\" \/>\n<meta property=\"og:site_name\" content=\"ANGULARarchitects\" \/>\n<meta property=\"article:published_time\" content=\"2024-01-25T07:07:29+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-01-26T17:29:59+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/shutterstock_2332068167.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1000\" \/>\n\t<meta property=\"og:image:height\" content=\"667\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Rainer Hahnekamp\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Rainer Hahnekamp\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"4 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/\"},\"author\":{\"name\":\"Rainer Hahnekamp\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/652ef3ff751c052d51f2640fecdfa30e\"},\"headline\":\"Testing Angular&#8217;s Latest Features\",\"datePublished\":\"2024-01-25T07:07:29+00:00\",\"dateModified\":\"2024-01-26T17:29:59+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/\"},\"wordCount\":676,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/shutterstock_2332068167.jpg\",\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/\",\"name\":\"Testing Angular's Latest Features - ANGULARarchitects\",\"isPartOf\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/shutterstock_2332068167.jpg\",\"datePublished\":\"2024-01-25T07:07:29+00:00\",\"dateModified\":\"2024-01-26T17:29:59+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#primaryimage\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/shutterstock_2332068167.jpg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/shutterstock_2332068167.jpg\",\"width\":1000,\"height\":667},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.angulararchitects.io\/en\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Testing Angular&#8217;s Latest Features\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#website\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"name\":\"ANGULARarchitects\",\"description\":\"AngularArchitects.io\",\"publisher\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#organization\",\"name\":\"ANGULARarchitects\",\"alternateName\":\"SOFTWAREarchitects\",\"url\":\"https:\/\/www.angulararchitects.io\/en\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"contentUrl\":\"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg\",\"width\":644,\"height\":216,\"caption\":\"ANGULARarchitects\"},\"image\":{\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/github.com\/angular-architects\",\"https:\/\/www.linkedin.com\/company\/angular-architects\/\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/652ef3ff751c052d51f2640fecdfa30e\",\"name\":\"Rainer Hahnekamp\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/4afdaa50c6ae20dbdcb5c5f62e0070b02b059a76cb63e56f22c3475fbb320890?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/4afdaa50c6ae20dbdcb5c5f62e0070b02b059a76cb63e56f22c3475fbb320890?s=96&d=mm&r=g\",\"caption\":\"Rainer Hahnekamp\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Testing Angular's Latest Features - ANGULARarchitects","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/","og_locale":"en_US","og_type":"article","og_title":"Testing Angular's Latest Features - ANGULARarchitects","og_description":"1. Standalone &amp; Mocking If you prefer the kind of tests where you minimize your mock as much as possible, you will be quite happy with Standalone Components. Forgotten are the days when we had to carefully select only those dependencies from an NgModule that belong to the component under test and move it over [&hellip;]","og_url":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/","og_site_name":"ANGULARarchitects","article_published_time":"2024-01-25T07:07:29+00:00","article_modified_time":"2024-01-26T17:29:59+00:00","og_image":[{"width":1000,"height":667,"url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/shutterstock_2332068167.jpg","type":"image\/jpeg"}],"author":"Rainer Hahnekamp","twitter_card":"summary_large_image","twitter_misc":{"Written by":"Rainer Hahnekamp","Est. reading time":"4 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#article","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/"},"author":{"name":"Rainer Hahnekamp","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/652ef3ff751c052d51f2640fecdfa30e"},"headline":"Testing Angular&#8217;s Latest Features","datePublished":"2024-01-25T07:07:29+00:00","dateModified":"2024-01-26T17:29:59+00:00","mainEntityOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/"},"wordCount":676,"commentCount":0,"publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/shutterstock_2332068167.jpg","inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/","url":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/","name":"Testing Angular's Latest Features - ANGULARarchitects","isPartOf":{"@id":"https:\/\/www.angulararchitects.io\/en\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#primaryimage"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#primaryimage"},"thumbnailUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/shutterstock_2332068167.jpg","datePublished":"2024-01-25T07:07:29+00:00","dateModified":"2024-01-26T17:29:59+00:00","breadcrumb":{"@id":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#primaryimage","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/shutterstock_2332068167.jpg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2024\/01\/shutterstock_2332068167.jpg","width":1000,"height":667},{"@type":"BreadcrumbList","@id":"https:\/\/www.angulararchitects.io\/en\/blog\/testing-angulars-latest-features\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.angulararchitects.io\/en\/"},{"@type":"ListItem","position":2,"name":"Testing Angular&#8217;s Latest Features"}]},{"@type":"WebSite","@id":"https:\/\/www.angulararchitects.io\/en\/#website","url":"https:\/\/www.angulararchitects.io\/en\/","name":"ANGULARarchitects","description":"AngularArchitects.io","publisher":{"@id":"https:\/\/www.angulararchitects.io\/en\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.angulararchitects.io\/en\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.angulararchitects.io\/en\/#organization","name":"ANGULARarchitects","alternateName":"SOFTWAREarchitects","url":"https:\/\/www.angulararchitects.io\/en\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/","url":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","contentUrl":"https:\/\/www.angulararchitects.io\/wp-content\/uploads\/2023\/07\/AA-Logo-RGB-horizontal-inside-knowledge-black.svg","width":644,"height":216,"caption":"ANGULARarchitects"},"image":{"@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/github.com\/angular-architects","https:\/\/www.linkedin.com\/company\/angular-architects\/"]},{"@type":"Person","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/652ef3ff751c052d51f2640fecdfa30e","name":"Rainer Hahnekamp","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.angulararchitects.io\/en\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/4afdaa50c6ae20dbdcb5c5f62e0070b02b059a76cb63e56f22c3475fbb320890?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/4afdaa50c6ae20dbdcb5c5f62e0070b02b059a76cb63e56f22c3475fbb320890?s=96&d=mm&r=g","caption":"Rainer Hahnekamp"}}]}},"_links":{"self":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/24334","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/users\/13"}],"replies":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/comments?post=24334"}],"version-history":[{"count":2,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/24334\/revisions"}],"predecessor-version":[{"id":24548,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/posts\/24334\/revisions\/24548"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media\/24331"}],"wp:attachment":[{"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/media?parent=24334"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/categories?post=24334"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.angulararchitects.io\/en\/wp-json\/wp\/v2\/tags?post=24334"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}