<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Mark Blandford</title>
    <description>The latest articles on Forem by Mark Blandford (@markblandford).</description>
    <link>https://forem.com/markblandford</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1185642%2Fcbdfb4ff-d458-4f6e-afdd-af148c784fd3.png</url>
      <title>Forem: Mark Blandford</title>
      <link>https://forem.com/markblandford</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/markblandford"/>
    <language>en</language>
    <item>
      <title>Threat Modelling with Threat Dragon</title>
      <dc:creator>Mark Blandford</dc:creator>
      <pubDate>Sat, 16 Aug 2025 12:20:00 +0000</pubDate>
      <link>https://forem.com/markblandford/threat-modelling-with-threat-dragon-3nda</link>
      <guid>https://forem.com/markblandford/threat-modelling-with-threat-dragon-3nda</guid>
      <description>&lt;p&gt;This post is about how to perform Threat Modelling using the &lt;a href="https://en.wikipedia.org/wiki/STRIDE_model" rel="noopener noreferrer"&gt;STRIDE model&lt;/a&gt;. I'll also highlight some tips with using &lt;a href="https://owasp.org/www-project-threat-dragon/" rel="noopener noreferrer"&gt;OWASP Threat Dragon&lt;/a&gt;, and some practices I choose to follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Threat Modelling
&lt;/h2&gt;

&lt;p&gt;Threat modelling is a critical part of a secure development lifecycle. We use a model, such as &lt;a href="https://en.wikipedia.org/wiki/STRIDE_model" rel="noopener noreferrer"&gt;STRIDE&lt;/a&gt;, to help us identify, review and mitigate potential threats and vulnerabilities within a software system (or application).&lt;/p&gt;

&lt;p&gt;The goal is to identify and mitigate threats early, before they become problems. It is critical that everyone in the team responsible for the software application is involved, as everyone can bring a different perspective. Furthermore, it helps educate all members of the team, as application security is everyone's responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  The STRIDE Threat Model
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/STRIDE_model" rel="noopener noreferrer"&gt;STRIDE&lt;/a&gt; is a threat classification model which was developed by Microsoft.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;S&lt;/strong&gt;poofing&lt;/td&gt;
&lt;td&gt;Impersonating something or someone else.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;T&lt;/strong&gt;ampering&lt;/td&gt;
&lt;td&gt;Modifying data or code, in transit or at rest.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;R&lt;/strong&gt;epudiation&lt;/td&gt;
&lt;td&gt;Denying an action or transaction has taken place. Denying without accountability.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;I&lt;/strong&gt;nformation Disclosure&lt;/td&gt;
&lt;td&gt;Exposing data to unauthorised parties.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;D&lt;/strong&gt;enial of Service (DoS)&lt;/td&gt;
&lt;td&gt;Preventing legitimate people or processes from access a service or resource.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;E&lt;/strong&gt;levation of Privilege&lt;/td&gt;
&lt;td&gt;Gaining unauthorised access to resources or functionality.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each element within the system can be exposed to different threats in different ways. Furthermore, they could be exposed to multiple 'implementations' of the same threat. For example Denial of Service against a process, could come from a DDos attack or from an internal, malicious actor. Each would be represented as a different threat on that process.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;

&lt;h3&gt;
  
  
  STRIDE Threat Classification Overview
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Key Question&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spoofing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Impersonating others or systems.&lt;/td&gt;
&lt;td&gt;&lt;em&gt;"Can someone pretend to be someone else?"&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tampering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Modifying data or code.&lt;/td&gt;
&lt;td&gt;&lt;em&gt;"Can someone alter information?"&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repudiation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Denying actions occurred.&lt;/td&gt;
&lt;td&gt;&lt;em&gt;"Can someone deny they did something?"&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Information Disclosure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Exposing sensitive data.&lt;/td&gt;
&lt;td&gt;&lt;em&gt;"Can someone see information they shouldn't?"&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Denial of Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Blocking legitimate access.&lt;/td&gt;
&lt;td&gt;&lt;em&gt;"Can someone prevent the system from working?"&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Elevation of Privilege&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gaining unauthorised access.&lt;/td&gt;
&lt;td&gt;&lt;em&gt;"Can someone do things they're not allowed to?"&lt;/em&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Threats by Threat Dragon Element
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Element&lt;/th&gt;
&lt;th&gt;Applicable Threats&lt;/th&gt;
&lt;th&gt;Common Examples&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;🧑 Actor&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;None directly&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Customers, other systems.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;↩️ Data Flow&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;T&lt;/strong&gt;ampering, &lt;strong&gt;I&lt;/strong&gt;nfo Disclosure, &lt;strong&gt;D&lt;/strong&gt;oS&lt;/td&gt;
&lt;td&gt;HTTP requests, database queries, API calls.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;📦 Process&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;All STRIDE threats&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Web apps, APIs, microservices.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;📁 Store&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;T&lt;/strong&gt;ampering, &lt;strong&gt;R&lt;/strong&gt;epudiation, &lt;strong&gt;I&lt;/strong&gt;nfo Disclosure, &lt;strong&gt;D&lt;/strong&gt;oS&lt;/td&gt;
&lt;td&gt;Databases, file systems, browser storage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;🤝 Trust Boundary&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Defines the threat context&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Network perimeters, different infrastructure.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Common Mitigations
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat Type&lt;/th&gt;
&lt;th&gt;Typical Mitigations&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spoofing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MFA, authentication.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tampering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encryption, input validation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repudiation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Audit logs, digital signatures.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Information Disclosure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Encryption, access controls, limit data stored.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Denial of Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Rate limiting, load balancing, monitoring.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Elevation of Privilege&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Role-Based Access controls, least privilege, regular reviews.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Threat Dragon
&lt;/h2&gt;

&lt;p&gt;Threat Dragon is a free, open-source threat modelling tool developed by &lt;a href="https://owasp.org/about/" rel="noopener noreferrer"&gt;OWASP&lt;/a&gt;. It enables teams to visually develop the data flows of a system, and document threats and mitigations. It supports &lt;a href="https://en.wikipedia.org/wiki/STRIDE_model" rel="noopener noreferrer"&gt;STRIDE&lt;/a&gt;, which this article focuses on, and other threat models. The output of Threat Dragon is not only visual, but is in JSON, so can be easily added to any source control system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Getting Started
&lt;/h3&gt;

&lt;p&gt;Threat Dragon is available in two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;🌐 Web version - Simply use within the browser at &lt;a href="https://threatdragon.com" rel="noopener noreferrer"&gt;https://threatdragon.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;💻 Desktop version - Available for download from the &lt;a href="https://github.com/OWASP/threat-dragon/releases" rel="noopener noreferrer"&gt;OWASP Threat Dragon GitHub release page&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simply create a new model and start adding elements - Threat Dragon will suggest applicable STRIDE threats for each component you add.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tips with using Threat Dragon
&lt;/h3&gt;

&lt;p&gt;Threat Dragon is a little temperamental but once you get use to it, it is a great tool.&lt;/p&gt;

&lt;h4&gt;
  
  
  Interface Navigation
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Save frequently&lt;/strong&gt; - Models can be lost easily and saves sometimes fail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Click arrow heads on Data Flows&lt;/strong&gt; to select data flows (not the line itself).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Click anywhere on Data Flows&lt;/strong&gt; to add corners/drag points.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Threat Analysis Best Practices
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Different elements have different threats&lt;/strong&gt; - A Tampering threat on a Data Flow is different compared to a Process or Store. The mitigations will be different too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Include all applicable threats&lt;/strong&gt; - Even mark as N/A to show they were considered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mark out-of-scope elements&lt;/strong&gt; - Document dependencies you can't control, but be aware they may change behaviour after marking as out-of-scope.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review regularly&lt;/strong&gt; - Threats change as systems evolve, especially when adding new integrations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Troubleshooting
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Models are JSON underneath - use JSON linting tools if Threat Dragon shows errors.

&lt;ul&gt;
&lt;li&gt;You can manually edit JSON files for bulk changes (find/replace for renaming).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  An Example
&lt;/h2&gt;

&lt;p&gt;Here is a small, not very detailed example of a web application, which&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Uses a 3rd party authentication provider (out of scope).&lt;/li&gt;
&lt;li&gt;Communicates with a trusted API, which is considered a part of the software system.&lt;/li&gt;
&lt;li&gt;This API stores data in a database.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2nn1e3vmuh93nbfecmgu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2nn1e3vmuh93nbfecmgu.png" alt="Example Threat Model" width="722" height="509"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Steps
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Create the data flow / model of the application flow within your software system.

&lt;ol&gt;
&lt;li&gt;If it's large, consider breaking it down and create, smaller separate threat models for logical parts of the flow. Threat Dragon can have multiple models within the same 'parent' model.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;li&gt;Analysis each process, store &amp;amp; data flow following STRIDE.

&lt;ol&gt;
&lt;li&gt;When you have an element selected, clicking '+ New Threat' will at first default to adding the applicable threats for the element selected. However, you don't have to stick with this, feel free to add more as you see fit.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h3&gt;
  
  
  The documented threats for the example
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Out-of Scope
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Customer / browser - In this example, I've marked the customer as being out-of-scope as we cannot control customer behaviour or their browser. This will not always be the case however.&lt;/li&gt;
&lt;li&gt;3rd Party Auth Provider - We assume it is secure but fundamentally it is not our responsibility to ensure it is, as it is maintained by a 3rd, trusted party.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  Processes
&lt;/h4&gt;

&lt;h5&gt;
  
  
  Web App (frontend) 📦
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Risk&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Mitigation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spoofing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A malicious actor is able to gain access as another user of the application.&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;Tokens are only stored in secure, httpOnly, SameSite cookies so cannot be accessed by anything else. XSS &amp;amp; CSP is in use too.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tampering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Modifying data or code.&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;🔒 Code reviews, signed commits, and access controls are enforced. No data is stored with the Frontend application.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repudiation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Denying an action or transaction has taken place. Denying without accountability.&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;📜 Audit logs are maintained with user identifiers. All commits must be signed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Information Disclosure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Some, customer information is stored in the browser local storage.&lt;/td&gt;
&lt;td&gt;🔴 Open (High)&lt;/td&gt;
&lt;td&gt;Avoid using localStorage or sessionStorage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Denial of Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Preventing legitimate users from accessing a service or resource.&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;⚖️ Uses CDN caching and load balancing.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Elevation of Privilege&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gaining unauthorised access to resources or functionality.&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;🔑 Authorisation checks are carried out and there is no concept of roles within the application. Control to the infrastructure of the application and code is access controlled following least-privilege.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Backend API 📦
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Risk&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Mitigation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Spoofing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A malicious actor is able to gain access as another user of the application.&lt;/td&gt;
&lt;td&gt;🔴 Open (High)&lt;/td&gt;
&lt;td&gt;CORS is used to limit access to just the Web App domain. However, no authorisation checks are carried out on the requests using the JWT, meaning anyone with a valid JWT can impersonate another customer.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tampering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Modifying data or code.&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;🔒 Signed commits, code reviews, and access controls. No data is stored with the API.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repudiation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Denying an action or transaction has taken place. Denying without accountability.&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;📜 Log all requests with user ID from the JWT and associated actions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Information Disclosure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A 500 error response shows the stack trace, which exposes the language the API is written in etc.&lt;/td&gt;
&lt;td&gt;🔴 Open (Medium)&lt;/td&gt;
&lt;td&gt;Return generic or sanitised error responses.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Denial of Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Preventing legitimate people or processes from access a service or resource.&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;⚖️ Rate limiting, load balancing, and request timeouts.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Elevation of Privilege&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gaining unauthorised access to resources or functionality.&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;🔑 Role-based access control and least privilege enforced at the API code &amp;amp; infrastructure.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Stores
&lt;/h4&gt;

&lt;h5&gt;
  
  
  📁 Internal Database
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Risk&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Mitigation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tampering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Queries or stored data could be manipulated.&lt;/td&gt;
&lt;td&gt;🔴 Open (High)&lt;/td&gt;
&lt;td&gt;Use parameterised queries, restrict database accounts, and enforce strict schema validation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Repudiation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Denying an action or transaction has taken place. Denying without accountability.&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;📜 Database logs are maintained.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Information Disclosure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Personally identifiable information stored in plain text.&lt;/td&gt;
&lt;td&gt;🔴 Open (High)&lt;/td&gt;
&lt;td&gt;Encrypt sensitive fields at rest &amp;amp; while in use. Ensure there are strict access controls to the database.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Denial of Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Preventing legitimate people or processes from access a service or resource.&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;⚖️ Apply query limits, use indexes, and enable monitoring and alerts.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  ↔️ Data Flows
&lt;/h4&gt;

&lt;h5&gt;
  
  
  POST /api/{username}/order - 📦 Web App ↔ 📦 Backend API
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Key Issue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tampering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;TLS encryption.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Information Disclosure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🔴 Open (High)&lt;/td&gt;
&lt;td&gt;Username is in URL path.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Denial of Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;Rate limiting, API gateway.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Authenticate - 📦 Web App ↔ 📦 Third-Party Authentication Provider
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Key Issue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tampering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;TLS encryption.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Information Disclosure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;TLS with no sensitive data in request.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Denial of Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚪ N/A&lt;/td&gt;
&lt;td&gt;Third-party dependency risk. However, if they suffer a DoS, what impact will it have on us?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Verify JWT - 📦 Backend API ↔ 📦 Third-Party Authentication Provider
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Key Issue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tampering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🔴 Open (Medium)&lt;/td&gt;
&lt;td&gt;TLS 1.1 used (insecure)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Information Disclosure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🔴 Open (Low)&lt;/td&gt;
&lt;td&gt;TLS 1.1 used (insecure)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Denial of Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;⚪ N/A&lt;/td&gt;
&lt;td&gt;Third-party dependency risk. However, if they suffer a DoS, what impact will it have on us?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h5&gt;
  
  
  Queries - 📦 Backend API ↔ 📁 Internal Database
&lt;/h5&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Threat&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;th&gt;Key Issue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tampering&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🔴 Open (Low)&lt;/td&gt;
&lt;td&gt;No SSL on internal connections.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Information Disclosure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🔴 Open (Low)&lt;/td&gt;
&lt;td&gt;Credentials / queries unencrypted.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Denial of Service&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🟢 Mitigated&lt;/td&gt;
&lt;td&gt;Resource limits &amp;amp; monitoring.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Threat modelling with STRIDE and Threat Dragon can help teams to methodically analyse software systems, to help them identify vulnerabilities before they become issues.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key takeaways
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Include everyone&lt;/strong&gt; - Developers, architects, QAs, business stakeholders each bring unique perspectives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be comprehensive&lt;/strong&gt; - Document all threats, even those marked N/A, to demonstrate they have been considered.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review regularly&lt;/strong&gt; - Systems evolve, and so should the threat model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Threat Dragon may have its quirks, but it is free, accessible and fairly straight-forward to use. The JSON-based output is ideal for source control.&lt;/p&gt;

&lt;p&gt;Remember: the goal isn't for bullet-proof security, it is about understanding and documenting the known risks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Additional Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.gov.uk/government/publications/secure-connected-places-playbook-documents/conducting-a-stride-based-threat-analysis" rel="noopener noreferrer"&gt;GOV.UK - Conducting a STRIDE-based threat analysis&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://owasp.org/www-project-threat-dragon/" rel="noopener noreferrer"&gt;OWASP Threat Dragon project&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://owasp.org/www-community/Threat_Modeling_Process" rel="noopener noreferrer"&gt;OWASP Threat Modelling Process&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://owasp.org/www-community/Threat_Modeling_Process#stride" rel="noopener noreferrer"&gt;OWASP - STRIDE&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/STRIDE_model" rel="noopener noreferrer"&gt;STRIDE- Wikipedia&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>sdlc</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>No Spikes</title>
      <dc:creator>Mark Blandford</dc:creator>
      <pubDate>Tue, 21 Jan 2025 08:46:49 +0000</pubDate>
      <link>https://forem.com/markblandford/no-spikes-547m</link>
      <guid>https://forem.com/markblandford/no-spikes-547m</guid>
      <description>&lt;p&gt;I thought about writing this post a while ago but it has recently come to mind once again.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Spike is
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;...a timeboxed experiment meant to gather information to reduce risk and enable more accurate estimates for complex user stories.&lt;/p&gt;

&lt;p&gt;-- &lt;cite&gt;&lt;a href="https://agilemania.com/agile-spike-story-what-is-a-spike-in-agile" rel="noopener noreferrer"&gt;Agilemania - What is an Agile Spike Story?&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  I'm not a fan
&lt;/h2&gt;

&lt;p&gt;First off, let me be clear I'm not 100% against completing Spikes. Perhaps 99% against them though. The big issue I have is the tendency to over-rely on them for even the tiniest of uncertainty or lack of clarity or direction. I'm generally of the opinion an engineering team should be skilled and knowledgeable enough, plus have the confidence, to 'jump in' and engineer a solution. However, let's dig a little further.&lt;/p&gt;

&lt;h2&gt;
  
  
  The attributes of a Spike
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Time-boxed ⏳
&lt;/h3&gt;

&lt;p&gt;OK, so we're not commiting (if there is such as thing) to an estimate, but instead we will limit the time needed to gather the information required. How large is this 'time-box'?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A day?&lt;/li&gt;
&lt;li&gt;A week?&lt;/li&gt;
&lt;li&gt;A month?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we can come up with an accurate time-box, then we have some certainty of the unknown. Can that not be factored into the 'uncertainty' when estimating the actual Story?&lt;/p&gt;

&lt;h4&gt;
  
  
  What happens if the Spike gives us what we're looking for?
&lt;/h4&gt;

&lt;p&gt;Then what? The findings get written down, the prototype put to one-side (to decay) and the team switches context to something else. When the team then come back to start the implementation, are the findings of the Spike still accurate? Do they even make sense / do we understand them still / would the prototype still work? We probably need to spend even more time refreshing our memory.&lt;/p&gt;

&lt;h4&gt;
  
  
  What happens if we still don't have enough information at the end of the time-box?
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;Do we extend it or commit to a new Spike? - How long for? ⏱️&lt;/li&gt;
&lt;li&gt;Do we stop and do the &lt;em&gt;actual&lt;/em&gt; work? - So what was the point of the Spike if we are going to get going without all of the information anyway? We've just wasted the time it took to complete the Spike rather than just trying to get the work done.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Winner - Story&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Experiment 🧪
&lt;/h3&gt;

&lt;p&gt;This is possibly the only, possible case I can see for a Spike, but it's small. Even with using a Spike to experiment or prototype a solution, what happens when it's 'done'? What does 'Done' even look like? When is enough, enough?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Do we throw away the prototype? - If so, then we've probably wasted that time.&lt;/li&gt;
&lt;li&gt;Do we 'productionise' the prototype in the Story? - Probably could have avoided the Spike then and just done the work. If you're already neck-deep in it, it is costly to stop and come back later too.&lt;/li&gt;
&lt;li&gt;Do we document the findings of the experiment for later? - We've just introduced a huge context switch. Whoever picks up the actual work, which may not be the engineer who completed the Spike, may have a slight head-start but...

&lt;ul&gt;
&lt;li&gt;They may not understand the findings (another Spike perhaps? 🙂)&lt;/li&gt;
&lt;li&gt;Things might have changed since. Depending on how long ago the Spike was completed, the findings may no longer be relevant.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If an experiment is to be carried out because there are two or more potential solutions, which all deliver the value in the Story, why not simply implement the most simple solution (MVP) and iterate on it?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner - Story (a little closer)&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Gather Information 💭
&lt;/h3&gt;

&lt;p&gt;I'm a strong believer in empowering Software Engineers to engineer solutions. I would expect any engineer worth their salt to be capable of gathering the information they need to complete a Story, often based on a simple, even potentially ambiguous problem statement (remember when a Story could fit on a Post-It?). However, if it is about gathering requirements, then that is &lt;strong&gt;not&lt;/strong&gt; the responsibility of the engineer and the request should not even be in the team yet.&lt;/p&gt;

&lt;p&gt;I'm struggling to think of occasions where there wouldn't at least be &lt;strong&gt;some&lt;/strong&gt; information to be able to start work on a Story. After all, the ACs should be enough to get going and figure it out as you go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner - Story&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Reduce Risk 🛡️
&lt;/h3&gt;

&lt;p&gt;OK, so we're completing a Spike because we don't know if something is technically feasible or something similar. I.e. you don't want to commit to an implementation that may not work. If your team already aims to deliver MVP and iterate on it, what is the harm in trying? You should know fairly quickly whether something is going to work, if it is great, continue. If not, stop and do something else. Doing this as a Spike, doesn't really change the 'risk', it just reduces our agility and makes us slower to implement a solution. If something is &lt;em&gt;so&lt;/em&gt; unknown is it with the &lt;em&gt;right&lt;/em&gt; team to do it (does the team have the necessary skills)?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner - Story&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  More accurate Estimates
&lt;/h3&gt;

&lt;p&gt;Who cares? An estimate should not be a measure of time or used to measure the output or success of the team. In my opinion an estimate is made up of three components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Development / Test Effort&lt;/li&gt;
&lt;li&gt;Complexity&lt;/li&gt;
&lt;li&gt;Uncertainty&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I guess you might complete a Spike to try and reduce the uncertainty. Why not simply adjust your estimate of the Story instead? If you're uncertain, perhaps the estimate (in Story Points, Fibonacci Sequence) from a 3 to a 5, or perhaps a 5 to an 8. An estimate is just that and should reflect what is known at that point in time. It's why a team should ideally estimate their Stories only shortly before they plan to work on them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner - Story&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Complex Stories
&lt;/h3&gt;

&lt;p&gt;If the story is as small as it can be to still offer value to the customer, does it matter if it's complex? Again, the level of perceived complexity should be a factor of the Story's estimate. Great engineers should thrive with overcoming complexity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Winner - Story&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I can probably count on one hand the number of times when I've genuinely felt a Spike was needed because the path was so unknown or brand-new then determining whether something could even ever be done was unclear. Perhaps it's just the type of work I do but it has probably been years since I've seen the value in a Spike.&lt;/p&gt;

&lt;p&gt;If a Spike is needed to develop the 'gold-standard' of requirements or an approach to a solution, isn't that Waterfall? At the very least it reduces the agility of the team.&lt;/p&gt;

</description>
      <category>agile</category>
    </item>
    <item>
      <title>I have &lt;insert alternative noun&gt; not Users</title>
      <dc:creator>Mark Blandford</dc:creator>
      <pubDate>Sun, 21 Apr 2024 07:19:51 +0000</pubDate>
      <link>https://forem.com/markblandford/i-have-not-users-52l5</link>
      <guid>https://forem.com/markblandford/i-have-not-users-52l5</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;...there are only two industries which refer to their customers as users, drugs and computers.&lt;/p&gt;

&lt;p&gt;-- &lt;cite&gt;&lt;a href="https://www.edwardtufte.com/tufte/complit_9497"&gt;Edward Tufte&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I first heard this quote a number of years ago, and it has since been misquoted and misused, notably replacing 'drugs' with 'illegal drugs'. However, it still rings true to me. Why do we refer to those that utilise our software, web sites, services etc. as 'users' rather than:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customers&lt;/li&gt;
&lt;li&gt;Clients&lt;/li&gt;
&lt;li&gt;Visitor&lt;/li&gt;
&lt;li&gt;Consumer&lt;/li&gt;
&lt;li&gt;Patron (probably not, but you get the idea)&lt;/li&gt;
&lt;li&gt;Anything else that is specific to your application. In finance perhaps, you might use 'Adviser' for example.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I can't explain why we do.&lt;/p&gt;

&lt;p&gt;Now consider the negative conatations of the word 'user' when in the context of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;...there are only two industries which refer to their customers as users, drugs and computers.&lt;/p&gt;

&lt;p&gt;-- &lt;cite&gt;&lt;a href="https://www.edwardtufte.com/tufte/complit_9497"&gt;Edward Tufte&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Add in the growing research and concern around the addictive nature of certain 'computers' or technology such as &lt;a href="https://www.ukat.co.uk/addiction/behavioural/social-media/"&gt;Social Media&lt;/a&gt;, the parallels are certainly there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time for a different approach
&lt;/h2&gt;

&lt;p&gt;For a while now I have been making a concerted effort to not use the word 'users' when referring to those whom use the technology I develop. Don't get me wrong, it is a hard habit to break. I have found however, it feels like I'm becoming more familiar and aware of those that do. It's as if by referring to those 'users' by another noun, the real persona of the individual develops.&lt;/p&gt;

&lt;p&gt;In practicing this, I've also noticed something else: when we all refer to those that use technology as 'users', it becomes difficult having conversations with each other. For example,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dave&lt;/strong&gt;: Hi, a &lt;strong&gt;user&lt;/strong&gt; has submitted order #123. I can see from our logs this order hit your system, but we don't know what's happened. Can you find out?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jane:&lt;/strong&gt; Hi Dave, yep I can see there was an issue with the &lt;strong&gt;user's&lt;/strong&gt; order. A &lt;strong&gt;user&lt;/strong&gt; has checked-out the order on the system to investigate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dave:&lt;/strong&gt; Thank you Jane, when will the user get an update?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jane:&lt;/strong&gt; ???&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;See what I mean? If everyone is talking about users, it can quickly become unclear about whom everyone is talking about! If we switch our terminology instead to 'Customer' and 'Sales Agent', it becomes far clearer.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Dave&lt;/strong&gt;: Hi, a &lt;strong&gt;customer&lt;/strong&gt; has submitted order #123. I can see from our logs this order hit your system, but we don't know what's happened. Can you find out?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jane:&lt;/strong&gt; Hi Dave, yep I can see there was an issue with the &lt;strong&gt;customer's&lt;/strong&gt; order. A &lt;strong&gt;sales agent&lt;/strong&gt; has checked-out the order on the system to investigate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dave:&lt;/strong&gt; Thank you Jane, when will the &lt;strong&gt;customer&lt;/strong&gt; get an update?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jane:&lt;/strong&gt; Not sure, I'll ping a message to the &lt;strong&gt;agent&lt;/strong&gt; for an update.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Personas
&lt;/h2&gt;

&lt;p&gt;When I refer to the 'clients' of an application, I'm able to better describe and empathise with their experience as well as better illustrate the purpose of the application itself. For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When a &lt;strong&gt;user&lt;/strong&gt; is on the checkout page, they are seeing an error...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now substitute the word "User" with "customer"...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When a &lt;strong&gt;customer&lt;/strong&gt; is on the checkout page, they are seeing an error...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It gives a different meaning and importance to the sentence. By changing that one word, we immediately understand more about what is going on and what the consequences could be. For me the differences are:&lt;/p&gt;

&lt;p&gt;A customer is someone who:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is engaged.&lt;/li&gt;
&lt;li&gt;Wants to 'give us money'! But we can lose money, if we provide a poor experience.&lt;/li&gt;
&lt;li&gt;Will possibly come back if they have a great experience. Or won't if they don't.&lt;/li&gt;
&lt;li&gt;provides an opportunity for us to build a relationship with.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A user however feels more passive and less engaged. It doesn't immediately describe what they want or their purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;I think if everyone in technology were to transition away to referring to the consumers of their tech, applications, services, web sites etc. to nouns more reflective of the customer's experience / persona, we will build better, more engaging and valuable tech.&lt;/p&gt;

&lt;p&gt;The next time you go to refer to a 'user' of your web site, pause. Are they a visitor or perhaps a customer? Depending on your response, may just change how you think about and treat them and their experience.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>discuss</category>
      <category>ux</category>
    </item>
    <item>
      <title>How I practice TDD</title>
      <dc:creator>Mark Blandford</dc:creator>
      <pubDate>Sat, 18 Nov 2023 09:08:17 +0000</pubDate>
      <link>https://forem.com/markblandford/how-i-practice-tdd-13jk</link>
      <guid>https://forem.com/markblandford/how-i-practice-tdd-13jk</guid>
      <description>&lt;h2&gt;
  
  
  What is TDD?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Test-driven development (TDD) is a software development process relying on software requirements being converted to test cases before software is fully developed, and tracking all software development by repeatedly testing the software against all test cases. This is as opposed to software being developed first and test cases created later.&lt;/p&gt;

&lt;p&gt;-- &lt;cite&gt;&lt;a href="https://en.wikipedia.org/wiki/Test-driven_development"&gt;Wikipedia - Test-driven development&lt;/a&gt;&lt;cite&gt;&lt;/cite&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In essence, write the tests first and then the production code.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Red/green/refactor - the TDD mantra.&lt;/p&gt;

&lt;p&gt;-- &lt;cite&gt;&lt;a href="https://www.oreilly.com/library/view/test-driven-development/0321146530/"&gt;Test-driven development: by Example - Kent Beck&lt;/a&gt;&lt;cite&gt;&lt;/cite&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Red - You write a test that doesn't work (it probably won't even compile as the Unit Under Test doesn't [yet] exist).&lt;/li&gt;
&lt;li&gt;Green - Make the test work - You build the Unit Under Test / production code.&lt;/li&gt;
&lt;li&gt;Refactor - clean up the test, removing duplication etc. (but please keep the test &lt;a href="https://en.wikipedia.org/wiki/Don't_repeat_yourself#AHA"&gt;'DAMP' / 'AHA'&lt;/a&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Some of the feedback I hear about why Engineers don't TDD
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;TDD is slower&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I disagree. If your team has a mentality for quality software, then you already must have a culture of automated testing. As such, why not build the tests first?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In fact, the opposite is actually true. I believe it has been proven / accepted that engineers who practice TDD, write more tests and are more productive.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When I don't write tests, development is faster&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yep, it sure would be: at first. Give it a few weeks or months and I expect you'll hit a wall, with confidence dwindling, that the software still works.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I don't like writing tests&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I get it. This is where TDD has helped me. I think practising TDD, makes you better at writing tests and as such, the aversion evaporates. It's still writing code, so should be enjoyable. I think writing the tests first make the process feel less like a chore.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TDD is hard&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we're getting somewhere. I think you're right, at least at first. In my experience, this differs between languages and competence too. For example, I began practising TDD in C#, an OO language. I found the strictness comforting and because of my level of experience at the time, it was easier.&lt;/p&gt;

&lt;p&gt;Then, when I started developing Angular applications, I simply couldn't use TDD as I had been use to. I had to adapt. I didn't know the language &amp;amp; test framework well enough. I struggled with the 'laziness' of the language and the &lt;a href="https://dev.to/markblandford/the-venn-of-angular-component-testing-the-grey-area-4oi7"&gt;blurred lines&lt;/a&gt; of the &lt;em&gt;unit&lt;/em&gt; testing suggested. Likewise, when I develop in Python, I try but I cannot do 'full' Test Driven Development but I try. I still get some benefit from the little I can do. This brings us onto the next section, the benefits I gain from TDD.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Benefits I get from TDD
&lt;/h2&gt;

&lt;p&gt;How many engineers plan the work they are about to develop? I bet most of you don't. This covers the first, three benefits I gain from TDD:&lt;/p&gt;

&lt;p&gt;1️⃣ TDD helps me plan / acts as a To-Do list for the work ahead.&lt;br&gt;
2️⃣ TDD forces me to break a task into smaller pieces (units).&lt;br&gt;
3️⃣ This process immediately makes me think about how the feature is going to work and even raise any questions if the requirements aren't clear. I find this really beneficial, as it means any clarification on the requirements is completed right at the start, and not when you're already head-deep in the code.&lt;/p&gt;

&lt;p&gt;How many times have you been working on a new feature, and got to a point where you've added a little extra&lt;sup id="fnref1"&gt;1&lt;/sup&gt; as you think it will be required in the future?&lt;/p&gt;

&lt;p&gt;4️⃣ TDD stops me developing more than what has been requested. It stops me trying to predict the future.&lt;/p&gt;

&lt;p&gt;However:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you get today's work done today, but you do it in such a way that you can't possibly get tomorrow's work done tomorrow, then you lose.&lt;/p&gt;

&lt;p&gt;-- &lt;cite&gt;Kent Beck (excerpt from Refactoring by Martin Fowler)&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I bet most engineers, who don't subscribe to TDD, have gotten to where the production code is done, and they've come to write the tests and found the code isn't easily testable. This probably means your code doesn't adhere to some well regarded development patterns. &lt;strong&gt;This doesn't mean, make all of the methods public!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;5️⃣ TDD helps me write better code. This often means it is more functional with few side-effects.&lt;br&gt;
6️⃣ TDD also helps me learn and get more comfortable with the test frameworks: I actually enjoy writing tests!&lt;/p&gt;

&lt;p&gt;Similar, you've written the production code, you're beat, you're done. The last thing you want to do is write tests. You're validated further when you tell your business stakeholders, &lt;em&gt;"the work is done I just have the tests to write"&lt;/em&gt;. They're response: &lt;em&gt;"don't bother, we need that feature out"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;7️⃣ I've written the tests up-front, and I get that regular feedback, as the tests go from failing to passing.&lt;br&gt;
8️⃣ TDD gives me confidence that when all the tests are green 🟢, I've completed the full, working feature. There have been times where I have such confidence, I don't even need to manually test the changes: I know it works because my tests tell me so.&lt;/p&gt;
&lt;h2&gt;
  
  
  When to TDD
&lt;/h2&gt;

&lt;p&gt;I think there are occasions when it is difficult or not possible to practice 'full' TDD &lt;em&gt;all of the time&lt;/em&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If you don't know or cannot predict the result of a scenario, then I don't think you can TDD.&lt;/li&gt;
&lt;li&gt;If you don't have an idea of how to get to the result / outcome, then you probably can't TDD. For example, if you need to 'trial and error' to get there. However, you maybe able to figure it out as you go (so try and start with TDD).&lt;/li&gt;
&lt;li&gt;If you simply don't know how to write tests, then you can't practice TDD. Are you ready to develop fully featured, robust &amp;amp; maintainable software in that case?&lt;/li&gt;
&lt;li&gt;If you don't have a solid understanding of the language, then TDD will be hard, but again I think you can start.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It could be argued that none of these points actually highlight a problem with Test-Driven Development. These points potentially highlight problems with the development / design approach of an engineer.&lt;/p&gt;
&lt;h2&gt;
  
  
  How I TDD
&lt;/h2&gt;

&lt;p&gt;Let's go through two examples, for TDD with unit tests:  one in Angular and another in Java.&lt;/p&gt;
&lt;h3&gt;
  
  
  Angular
&lt;/h3&gt;
&lt;h4&gt;
  
  
  The UI Requirement
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;I want to be able to change the colour scheme on my website, between three different themes: Default, Night &amp;amp; Sunny.&lt;/p&gt;

&lt;p&gt;At this time, the specs of the themes are out of scope but will be controlled depending on the value of &lt;code&gt;data-theme&lt;/code&gt; attribute on the &lt;code&gt;html&lt;/code&gt; element.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;
  
  
  What we need to develop for the UI feature
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;An Angular Service to update the &lt;code&gt;data-theme&lt;/code&gt; attribute on the &lt;code&gt;html&lt;/code&gt; element.&lt;/li&gt;
&lt;li&gt;a UI element for the customer to select one of the three themes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the brevity of this post, we'll just address step 1, the Angular Service. This is what I have on &lt;a href="https://www.blandford.dev"&gt;my own website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With these requirements, let's now write our 'to-do' list but as unit tests &amp;amp; test setup:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ThemeService&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should set the html data-theme attribute to the provided theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MockedObject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ThemeService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not implemented&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This test describes what we have to implement:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We need a new Angular Service.&lt;/li&gt;
&lt;li&gt;The service has a dependency on the DOM / &lt;code&gt;Document&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The service will need a public method to enable the selected theme.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now, we know that if we even attempt to run the test, it'll fail immediately because &lt;code&gt;ThemeService&lt;/code&gt; doesn't exist. 🔴&lt;/p&gt;

&lt;p&gt;I always ensure too, that my new test throws a 'Not Implemented' exception. Test Frameworks generally mark tests as passed 🟢, if there is no assertion. Ensuring the test throws an exception, immediately means the test is 🔴 and I don't have a false-sense that everything is done.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;💡&lt;strong&gt;Tip:&lt;/strong&gt; I have added shortcuts / code snippets in my IDEs, to enable me to quickly insert a 'Not implemented' exception. I can type nie + tab and depending on the language, I get the new exception added.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Next, I actually create the Production Service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;ThemeService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(@&lt;/span&gt;&lt;span class="nd"&gt;Inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DOCUMENT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, I can now run the test. The spec will now compile but thanks to the &lt;code&gt;throw new Error('Not implemented')&lt;/code&gt;, will still fail. Now back to the test.&lt;/p&gt;

&lt;p&gt;I think I want a public method called, &lt;code&gt;enableTheme(theme: string)&lt;/code&gt;, which should take the theme and do as required:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;code&gt;querySelector()&lt;/code&gt; on the &lt;code&gt;Document&lt;/code&gt; to select the &lt;code&gt;html&lt;/code&gt; element.&lt;/li&gt;
&lt;li&gt;Call &lt;code&gt;setAttribute&lt;/code&gt; to set the &lt;code&gt;data-theme&lt;/code&gt; attribute with the provided theme.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This takes us to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Update the &lt;code&gt;dom&lt;/code&gt; Mock, to mock the &lt;code&gt;querySelector&lt;/code&gt; method, as we'll need to call it.&lt;/li&gt;
&lt;li&gt;Implement the rest of the test.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should set the html data-theme attribute to the provided theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ARRANGE&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MockedObject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;htmlStub&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;MockedObject&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLHtmlElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;spyOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;querySelector&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;mockReturnValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;htmlStub&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// ACT&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ThemeService&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enableTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sunny&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// ASSERT&lt;/span&gt;
  &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;htmlStub&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toHaveBeenCalledWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Sunny&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we implement the shell of the real, &lt;code&gt;enableTheme(theme: string)&lt;/code&gt; method, again with a 'Not implemented' exception:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;enableTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Not implemented&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test will now run but with the 'Not implemented' exception coming from the production code. We're getting there. Finally, we can implement the guts of the Unit Under Test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;enableTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Viola, we have a passing test, which means we have a fully implemented feature.&lt;/p&gt;

&lt;p&gt;Next, we'd refactor not only the test but also the production code, safe in the knowledge we have a working test to fallback on. For example, &lt;code&gt;theme&lt;/code&gt; would probably be better as an &lt;code&gt;enum&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Java Spring Boot
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The Integration Layer Requirement
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;I want to be able to serve the list of themes available to the UI, from our API, but where the themes are provided by a 3rd party at &lt;code&gt;https://example.com/themes&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The response should be a list of theme names, sorted alphabetically.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  What we need to develop for the API feature
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;A new, &lt;a href="https://github.com/OpenFeign/feign"&gt;Feign&lt;/a&gt; client, to make the &lt;code&gt;get&lt;/code&gt; request to &lt;code&gt;https://example.com/themes&lt;/code&gt;.

&lt;ol&gt;
&lt;li&gt;This client will expose a &lt;code&gt;get()&lt;/code&gt; method.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;A new Java service to call the new client.&lt;/li&gt;
&lt;li&gt;A new end-point on our controller which calls the new service.

&lt;ol&gt;
&lt;li&gt;This is what our UI will interface with.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For the brevity of this post, we'll just address step 2. the Java Service.&lt;/p&gt;

&lt;p&gt;With these requirements, let's now write our 'to-do' list but as unit tests &amp;amp; test setup.&lt;/p&gt;

&lt;p&gt;This leads to the following tests:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This code is untested&lt;/strong&gt; 😛&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomThemesServiceUnitTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;getThemes_themesAvailable_listofSortedThemes&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;getThemes_themesUnavailable_emptyStringArray&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// in reality, we would have more tests,&lt;/span&gt;
    &lt;span class="c1"&gt;// covering error responses etc.&lt;/span&gt;
    &lt;span class="c1"&gt;// because we're already thinking about these up-front&lt;/span&gt;
    &lt;span class="c1"&gt;// (and should be a part of our requirements too)&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Working backwards, these tests describe what we need to implement to achieve our objective:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We need a new Java Service, &lt;code&gt;CustomThemes&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The service will have a dependency on the soon-to-be developed, Feign client.

&lt;ol&gt;
&lt;li&gt;The Feign client will have a &lt;code&gt;get()&lt;/code&gt; method we need to call to retrieve the themes.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;The service will have a public, &lt;code&gt;getThemes()&lt;/code&gt; method.

&lt;ol&gt;
&lt;li&gt;The method will need to ensure the list is sorted alphabetically, when there are themes available.&lt;/li&gt;
&lt;li&gt;The method will need to elegantly handle when no themes are available.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Again, we know the tests will fail 🔴 immediately because of the 'Not implemented' exception.&lt;/p&gt;

&lt;p&gt;Next, I write the shell of the new service (for brevity I'm not including Spring Boot decorators, interfaces etc.).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomThemesService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;getThemes&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;NotImplementedException&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've not done much here, but it means we can continue with our tests, and they will at least compile. Now let's write the tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomThemesServiceUnitTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;getThemes_themesAvailable_listofSortedThemes&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ARRANGE&lt;/span&gt;
        &lt;span class="c1"&gt;// we will assume the `ThemeClient` interface has already been created&lt;/span&gt;
        &lt;span class="c1"&gt;// I'm using Mockito to mock the dependencies&lt;/span&gt;
        &lt;span class="nc"&gt;ThemeClient&lt;/span&gt; &lt;span class="n"&gt;mockThemeClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ThemeClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// not sorted, so we can verify the list is sorted&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;themes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"Sunny"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Default"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Night"&lt;/span&gt;&lt;span class="o"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockThemeClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// ACT&lt;/span&gt;
        &lt;span class="nc"&gt;CustomThemesService&lt;/span&gt; &lt;span class="n"&gt;cut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomThemesService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockThemeClient&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// ACT &amp;amp; ASSERT&lt;/span&gt;
        &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt; &lt;span class="s"&gt;"Default"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Night"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Sunny"&lt;/span&gt; &lt;span class="o"&gt;},&lt;/span&gt; &lt;span class="n"&gt;cut&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getThemes&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;getThemes_themesUnavailable_emptyStringArray&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// ARRANGE&lt;/span&gt;
        &lt;span class="nc"&gt;ThemeClient&lt;/span&gt; &lt;span class="n"&gt;mockThemeClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mock&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ThemeClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;when&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockThemeClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getThemes&lt;/span&gt;&lt;span class="o"&gt;()).&lt;/span&gt;&lt;span class="na"&gt;thenReturn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// ACT&lt;/span&gt;
        &lt;span class="nc"&gt;CustomThemesService&lt;/span&gt; &lt;span class="n"&gt;cut&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomThemesService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mockThemeClient&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// ACT &amp;amp; ASSERT&lt;/span&gt;
        &lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cut&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getThemes&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;length&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These look like some pretty good tests to me, that satisfy the requirements. If we run them, they will fail, as we have our 'Not implemented' exception in the Unit Under Test, so let's build that now.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomThemesService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ThemeClient&lt;/span&gt; &lt;span class="n"&gt;themeClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// we will assume the `ThemeClient` interface has already been created&lt;/span&gt;
    &lt;span class="nc"&gt;CustomThemesService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ThemeClient&lt;/span&gt; &lt;span class="n"&gt;themeClient&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;themeClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;themeClient&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;getThemes&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;themes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;themeClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// sort the themes&lt;/span&gt;
        &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we run our tests, now we should have one passing 🟢 and one failing 🔴. This is great and exactly what we want. Now to finish the implementation to handle when no themes are returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CustomThemesService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="nf"&gt;getThemes&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;themes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;themeClient&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;];&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// sort the themes&lt;/span&gt;
        &lt;span class="nc"&gt;Arrays&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sort&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;themes&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that should be it. Again, this is untested but looks about right to me. 🤞&lt;/p&gt;

&lt;h3&gt;
  
  
  Integration Tests
&lt;/h3&gt;

&lt;p&gt;I highly recommend TDD with Integration Tests. I actually find these easier, and depending on the Unit Under Test absolutely leads to better quality. By following TDD with Integration Tests also prevents me from forgetting to implement the tests, which I think happens frequently.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You're developing a new end-point for a Java API.

&lt;ol&gt;
&lt;li&gt;You already use a tool such as &lt;a href="https://rest-assured.io/"&gt;Rest-assured&lt;/a&gt; for your Integration Tests.&lt;/li&gt;
&lt;li&gt;You know what the new end-point is to be, what the responses are etc.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;You're developing a new Angular UI component.

&lt;ol&gt;
&lt;li&gt;You have the designs and know what HTML elements etc. should be used.&lt;/li&gt;
&lt;li&gt;You already use tools such as &lt;a href="https://playwright.dev/"&gt;Playwright&lt;/a&gt; or &lt;a href="https://www.cypress.io/"&gt;Cypress&lt;/a&gt; for your Integration Tests.&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In both of these situations, it is easy to write the Integration Tests. They won't work but when they finally do, you know you're dev complete. Furthermore, it'll make you think about the response permutations and potential edge-cases. Things, that may ordinarily get missed (or ignored later, when you're 'done') when diving head-first into the code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fixing defects or updating an existing feature
&lt;/h3&gt;

&lt;p&gt;When you need to squash a bug or update an existing feature the process is very similar.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run the tests &lt;em&gt;before&lt;/em&gt; you make any changes. All of the tests should pass 🟢. (If they don't you,ve got bigger problems).&lt;/li&gt;
&lt;li&gt;Update your existing tests, or create a new test(s) to cover the new, expected behaviour.&lt;/li&gt;
&lt;li&gt;Run your tests. The ones you have updated, should fail 🔴.&lt;/li&gt;
&lt;li&gt;Implement the changes required to the production code.&lt;/li&gt;
&lt;li&gt;Run your tests. If you've fixed the issue, then you tests are green 🟢 and you're done.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I get a real buzz when it comes to squashing a bug using this approach. Creating a test that asserts the expected behaviour to fix the bug, and then seeing that go green, is great. You know you've fixed it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Write the test shells, as a 'to-do' list, covering just the requirements.

&lt;ul&gt;
&lt;li&gt;Remember to throw your 'Not implemented' exceptions in the tests.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Implement the skeleton of the production code / Unit Under Test.

&lt;ul&gt;
&lt;li&gt;Remember to throw your 'Not implemented' exceptions in the Unit Under Test.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Write the body of the tests.

&lt;ul&gt;
&lt;li&gt;The tests will still fail 🔴, but with 'Not implemented' exceptions from the Unit Under Test.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Implement the production code in the Unit Under Test.&lt;/li&gt;
&lt;li&gt;The tests should now all pass 🟢.&lt;/li&gt;
&lt;li&gt;Go back around, and refactor / improve what has just been implemented with the confidence that you are covered with working tests.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I would guess that the majority of Software Engineers can at least complete steps 1 &amp;amp; 2, on the majority of the code they write. If you can start there, then I think in no time at all, you'll be able to complete all of the steps above. At the very least, your starting to use TDD concepts and I guarantee that will lead to better quality code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you've practiced TDD for your unit and integration tests, then do you need any manual testing?&lt;/strong&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;I always think back to a time when I built a really cool Easter Egg in a 404 error page. I was then pulled up by a more senior engineer, who (rightfully) challenged me on how I was going to test it. Needless to say, that 404 page never saw the light of day. Thank you, Richard, I'll never forget that valuable lesson! ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>testing</category>
      <category>tdd</category>
      <category>angular</category>
      <category>java</category>
    </item>
    <item>
      <title>The Venn of Angular Component Testing: Benefits &amp; summary</title>
      <dc:creator>Mark Blandford</dc:creator>
      <pubDate>Sun, 29 Oct 2023 12:00:00 +0000</pubDate>
      <link>https://forem.com/markblandford/the-venn-of-angular-component-testing-benefits-summary-5h7f</link>
      <guid>https://forem.com/markblandford/the-venn-of-angular-component-testing-benefits-summary-5h7f</guid>
      <description>&lt;p&gt;If you haven't read the previous parts of this series, please take a look otherwise what's to follow may not make too much sense.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Venn Diagram of Angular Component Testing
&lt;/h2&gt;

&lt;p&gt;This brings us to the heart of this series. Going back to the &lt;a href="https://dev.to/markblandford/the-venn-of-angular-component-testing-the-grey-area-4oi7#a-definition-of-an-integration-test"&gt;definition for an Integration Test from part 1 of this series&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[an Integration Test] Asserts a unit of software interacts as expected &lt;strong&gt;with another&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After what I've previously highlighted with Sociable Tests, I think Sociable, Angular Component Tests are actually just a type of &lt;em&gt;Integration Test&lt;/em&gt;. At the very least there is a significant overlap. Given this, &lt;strong&gt;we should treat and expect Sociable / Component DOM tests to behave as Integration Tests&lt;/strong&gt;: they are more fragile &amp;amp; slower.&lt;/p&gt;

&lt;p&gt;I'm sure if you've got this far though the series, then you are familiar with the &lt;a href="https://www.smashingmagazine.com/2023/09/long-live-test-pyramid/" rel="noopener noreferrer"&gt;Testing Pyramid&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4ci9d2bzk0n68ww4bdm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp4ci9d2bzk0n68ww4bdm.png" title="Test Pyramid" alt="Test Pyramid"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is my take on that but applying a similar principal to Angular, Component Testing.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61ihlw96ic6gf9huddwy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F61ihlw96ic6gf9huddwy.png" title="Venn Diagram of the number and make-up of Angular Component Tests" alt="Venn diagram of Angular Component Tests"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This highlights the overlap between Unit &amp;amp; Integration Tests when it comes to testing Angular Components and how the 'grey area' of testing &lt;em&gt;is&lt;/em&gt; the Sociable tests. It also emphasises the ratio (not to scale) of these types of tests I think we should be aiming for.&lt;/p&gt;

&lt;p&gt;If I could change three conventions regarding the general testing approach we take in Angular applications, it would be to have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;⏫ Have far more Solitary Unit Tests. Day-to-day I unfortunately see very few of these.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should test&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;//arrange&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyComponent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="c1"&gt;//act&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="c1"&gt;//assert&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;...).&lt;/span&gt;&lt;span class="nf"&gt;toEqual&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;🔼 More 'real' Integration Tests, that mimic real customer behaviour &amp;amp; interactions (using tools such as Playwright or Cypress).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;⏬ Fewer (near zero) Sociable / Component DOM Test Tests.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Should push engineers to have any logic within the class rather than the template.&lt;/li&gt;
&lt;li&gt;If you have to have them, then call them out. Put them in separate &lt;code&gt;describe('IT Tests'){}&lt;/code&gt; blocks or something similar (and limit the &lt;code&gt;TestBed&lt;/code&gt; boilerplate to that block).&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;p&gt;I think if an applications' test suite is organised in this way, it would lead to improved development practices. Engineers will lean more towards the &lt;a href="https://en.wikipedia.org/wiki/Don%27t_repeat_yourself" rel="noopener noreferrer"&gt;DRY&lt;/a&gt; principal and smaller, more distinct components which have a &lt;a href="https://en.wikipedia.org/wiki/Single-responsibility_principle" rel="noopener noreferrer"&gt;single responsibility&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rebuttal
&lt;/h2&gt;

&lt;p&gt;Given the 'Venn of Angular Component Testing' one could argue to simply just have a test suite of Sociable Tests, with no Solitary or Integration Tests. You absolutely &lt;em&gt;could&lt;/em&gt;. I've not tried but I imagine, at scale it would become unmanageable. I would strongly expect too, that you will have a suite of Integration tests using something like &lt;a href="https://playwright.dev" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; or &lt;a href="https://learn.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt;, so you'll end up duplicating your test effort.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Think about why we test interactions in the DOM? I think we do it to test how the customer is going to use our component / application. We already use tools like &lt;a href="https://playwright.dev" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; and &lt;a href="https://learn.cypress.io/" rel="noopener noreferrer"&gt;Cypress&lt;/a&gt; for our E2E Testing&lt;sup id="fnref1"&gt;1&lt;/sup&gt; which are probably better suited (and potentially less fragile than Sociable Tests) for this testing.&lt;/p&gt;

&lt;p&gt;How many times have you faced problems in the past with writing your [Sociable] unit tests to assert something in the DOM and you just cannot get it to work, no matter how many &lt;code&gt;fixture.detectChanges()&lt;/code&gt; you add? 😄 I know I've spent too much time down that rabbit hole when I know the code 'works'. It is probably quicker&lt;sup id="fnref2"&gt;2&lt;/sup&gt; (and easier) to extend the existing E2E / Integration tests to cover the new behaviour. Plus, if there isn't any, observable change in the DOM, why bother including it in a Sociable Test at all?&lt;/p&gt;

&lt;p&gt;Too often I see what could easily be Solitary Unit Tests written with all of the boilerplate of Sociable Tests without actually interacting with the Component Template. So the tests have none of the 'benefit' of a Sociable test but have all of the downsides of them. I've found by simply removing the &lt;code&gt;ComponentFixture&lt;/code&gt; etc., the tests run up to 10x faster.&lt;/p&gt;

&lt;p&gt;Final note: I was taught that if you're polluting the production code just so you can test something (think of the &lt;code&gt;data-testid&lt;/code&gt; attribute for one, or even simply changing the scope of a method to make it 'easier' to test), then you maybe doing something wrong or unconventional.&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;If you're not, then you should be. If you are performing &lt;em&gt;real&lt;/em&gt; E2E tests against &lt;em&gt;real&lt;/em&gt; APIs, then (👏) it shouldn't be too troublesome to reuse these tests to also execute against stubbed APIs (if you even need to bother). ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;To write, not to run. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;That's a whole other conversation however. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>angular</category>
      <category>testing</category>
    </item>
    <item>
      <title>The Venn of Angular Component Testing: Clearing the Grey</title>
      <dc:creator>Mark Blandford</dc:creator>
      <pubDate>Sun, 29 Oct 2023 09:24:52 +0000</pubDate>
      <link>https://forem.com/markblandford/the-venn-of-angular-component-testing-clearing-the-grey-4fcc</link>
      <guid>https://forem.com/markblandford/the-venn-of-angular-component-testing-clearing-the-grey-4fcc</guid>
      <description>&lt;p&gt;If you haven't read the previous parts to this series, please take a look as this provides some important background and context to the rest of this post.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sociable vs Solitary Unit Tests
&lt;/h2&gt;

&lt;p&gt;I think it was Jay Fields that coined the term Sociable &amp;amp; Solitary Tests. Martin Fowler has a &lt;a href="https://martinfowler.com/bliki/UnitTest.html"&gt;good article&lt;/a&gt; that mentions the concept too.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Solitary&lt;/strong&gt; Tests prefer to &lt;strong&gt;isolate&lt;/strong&gt; the unit under test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sociable&lt;/strong&gt; Tests often &lt;strong&gt;rely on&lt;/strong&gt; other units to fulfil the behaviour.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I think this more subtly describes the different approaches to 'unit testing' Angular components most Angular engineers take. Think about this too:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;[A Solitary Unit Test] Never cross boundaries&lt;/p&gt;

&lt;p&gt;-- &lt;cite&gt;&lt;a href="https://leanpub.com/wewut"&gt;'Working Effectively with Unit Tests' by Jay Fields&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I would consider the interface between the Component Template and the Component Class a boundary. Thus Component DOM Tests must be Sociable.&lt;/p&gt;

&lt;p&gt;Distilling this down further, I think this leads us to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🧩 &lt;strong&gt;Component Class Tests =&lt;/strong&gt; Solitary Unit Tests&lt;/p&gt;

&lt;p&gt;🤝 &lt;strong&gt;Component DOM Tests =&lt;/strong&gt; Sociable / Integration Tests&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I fall firmly into the Solitary camp of unit testing approaches: I can probably count on one hand the number of Angular, Sociable Unit Tests I have ever &lt;em&gt;had&lt;/em&gt; to write.&lt;/p&gt;

&lt;p&gt;My predisposition probably comes down to my development background. For example, it's simply not possible to test a WPF UI (or View) binding / interaction with the 'class' (View Model), with the unit test framework, &lt;a href="https://nunit.org/"&gt;NUnit&lt;/a&gt;. Furthermore, the 'View' should never have any logic anyway. I would argue, for the large part, it should be the same for Angular Templates too!&lt;/p&gt;

&lt;p&gt;In addition, the Sociable Tests exhibit traits similar to Integration tests for example: fragility &amp;amp; speed. To better describe this, look at this basic example.&lt;/p&gt;

&lt;h3&gt;
  
  
  An Example comparing Solitary &amp;amp; Sociable Angular Component Tests
&lt;/h3&gt;

&lt;p&gt;Take this Angular component as an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;button (click)="increment()"&amp;gt;Increment&amp;lt;/button&amp;gt;
    &amp;lt;p&amp;gt;{{ counterValue }}&amp;lt;/p&amp;gt;
  `&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;counterValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;counterIncremented&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counterValue&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counterIncremented&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here are the tests, written in the two different approaches:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Solitary Unit Tests&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AppComponent - Solitary Unit Tests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;when the button is clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should increment the counter value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counterValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counterValue&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should emit an event&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;emitted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counterIncremented&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;emitted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;increment&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emitted&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Sociable Tests&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AppComponent - Sociable Tests&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="na"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentFixture&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configureTestingModule&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;declarations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;

      &lt;span class="nx"&gt;fixture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TestBed&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createComponent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AppComponent&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;component&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;componentInstance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

      &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should display the initial counter value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counterValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displayedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;displayedValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="nx"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;should increment the counter when the button is clicked&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;amp;&lt;/span&gt;&lt;span class="nx"&gt;gt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;click&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detectChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displayedValue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fixture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;nativeElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;p&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;displayedValue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nx"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Notice how the Sociable Tests are subtly different - A Sociable Test wouldn't normally test the &lt;code&gt;counterIncremented&lt;/code&gt; event is dispatched. That would be on the responsibility of the parent 'subscriber' (which in turn shouldn't need to know under what circumstances it is emitted).&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;Where as the Solitary Unit Tests can be said to provide 100% coverage, the Sociable / Component DOM Tests can not. If you just had the Sociable Tests, you could delete the &lt;code&gt;this.counterIncremented.emit();&lt;/code&gt; line, which could be critical, and yet no tests would fail. 🙁&lt;/p&gt;

&lt;p&gt;Given these examples, let's outline the key differences:&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Differences: Solitary vs Sociable Tests
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;🧩 Solitary Test&lt;/th&gt;
&lt;th&gt;🤝 Sociable / Component DOM Test&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🔬 Isolated Units&lt;/td&gt;
&lt;td&gt;🌐 Component Interaction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🥸 None / Mocked&lt;/td&gt;
&lt;td&gt;🎨 DOM &amp;amp; Angular Change Detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;📐 Component Class logic&lt;/td&gt;
&lt;td&gt;🔄 Verify the Component Class and Template work together&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Coverage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;💯 High Unit Coverage&lt;/td&gt;
&lt;td&gt;〽️ Lower Coverage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fragility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;💪 Highly Resilient&lt;/td&gt;
&lt;td&gt;💀 More Fragile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speed (100 runs)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🚀 &lt;strong&gt;2.2ms Avg&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;🐌 26.9ms Avg&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;On Failure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🎯 Easy to Pinpoint&lt;/td&gt;
&lt;td&gt;🪨 Harder&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Fragility
&lt;/h4&gt;

&lt;p&gt;How many times have you added a new component, only to then find some, relatively unrelated suite of tests now fail or you see console warnings? Likely this is because another component test suite, imports an ancestor of your new component. When this happens, you're probably thinking "I know but that ancestor shouldn't care about my new grandchild component". I think you're right. It feels to me like, the ancestor test suite is more like an Integration Test suite, simply because it has a hierarchy of dependencies.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Sociable Unit Tests are more susceptible to cascading failures.&lt;/p&gt;

&lt;p&gt;-- &lt;cite&gt;&lt;a href="https://leanpub.com/wewut"&gt;'Working Effectively with Unit Tests' by Jay Fields&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This all comes down to using, the &lt;code&gt;TestBed&lt;/code&gt; or notably &lt;code&gt;TestBed.configureTestingModule()&lt;/code&gt;.&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;On the other hand, the Solitary Unit Tests can not suffer this same fate: they have zero knowledge of any descendants.&lt;/p&gt;

&lt;h4&gt;
  
  
  Speed
&lt;/h4&gt;

&lt;p&gt;Remember Unit Tests are meant to be fast. They should enable an engineer to be able to run them often, ideally after any change they make. Running two, Sociable Unit Tests in an average of 26.9ms is pretty good right? But how about running tests, that give you more coverage and greater confidence but &lt;strong&gt;12 times faster&lt;/strong&gt;? Just scale that up to when you have over 2,000 tests. That's the difference between the entire test run taking 27 seconds (which is still really fast but these are very simple tests) vs 2 seconds! That's what Solitary Unit Tests give you.&lt;/p&gt;

&lt;p&gt;If Unit Tests are meant to be fast, then I'll take running 2,000 of them in ~2 seconds please.&lt;/p&gt;

&lt;p&gt;Furthermore, I think most engineers find it quicker and easier to write Solitary Unit Tests than they do Sociable Unit Tests (Component DOM Tests), plus maintain the component dependencies in the tests.&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;In the final post in this series, I'll introduce the 'The Venn of Angular Component Testing'.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;You could of course wrap the component in the test with a dummy component but you are 100% then into Integration Test territory (as far as I'm concerned). Not to mention introducing additional complexity into the tests which is just something else that can break / fail your tests. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;Some of this can be overcome using mocking frameworks such as &lt;a href="https://github.com/help-me-mom/ng-mocks"&gt;ng-mocks&lt;/a&gt; but I struggle to then understand the value in the added boilerplate compared to simple Solitary Tests instead. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;How many console warnings (&lt;em&gt;"component A is not a known element"&lt;/em&gt;) do you see when you run your tests? Potentially hiding the warnings you really care about. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>angular</category>
      <category>testing</category>
    </item>
    <item>
      <title>The Venn of Angular Component Testing: The Grey Area</title>
      <dc:creator>Mark Blandford</dc:creator>
      <pubDate>Sun, 29 Oct 2023 09:06:05 +0000</pubDate>
      <link>https://forem.com/markblandford/the-venn-of-angular-component-testing-the-grey-area-4oi7</link>
      <guid>https://forem.com/markblandford/the-venn-of-angular-component-testing-the-grey-area-4oi7</guid>
      <description>&lt;p&gt;In my experience there is confusion between what I would consider a Unit Test and an Integration Test for an Angular Component. Drawing on my OO development experience and having practised Test-driven development for a number of years in Angular &amp;amp; Java (plus other languages &amp;amp; frameworks), I think I can help reduce the 'grey' in the landscape.&lt;/p&gt;

&lt;p&gt;This series is limited to just testing of Angular Components. I feel the concept of Unit Testing other Angular objects, such as Services is more well defined.&lt;/p&gt;

&lt;h2&gt;
  
  
  To Start
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A Definition of a Unit Test
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Asserts a unit of software works as expected &lt;strong&gt;within isolation&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  A Definition of an Integration Test
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Asserts a unit of software interacts as expected &lt;strong&gt;with another&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  What is the 'Unit' of a Unit Test?
&lt;/h3&gt;

&lt;p&gt;The smallest piece of software where, for a given change to an input there is an observed (to the software / unit under test) change in output. For example a public method, which for a given input leads to a predictable, visible output (to the software / unit under test).&lt;/p&gt;

&lt;h2&gt;
  
  
  A Comparison between a Unit Test &amp;amp; Integration Test
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;🧩 Unit Tests&lt;/th&gt;
&lt;th&gt;🤝 Integration Tests&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🔬 Isolated Units&lt;/td&gt;
&lt;td&gt;🌐 Component Interactions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependencies&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🧪 Mocked Dependencies&lt;/td&gt;
&lt;td&gt;🛠️ Real or Mocked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Isolated Code&lt;/td&gt;
&lt;td&gt;🔄 Component Integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Coverage&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;💯 High Unit Coverage&lt;/td&gt;
&lt;td&gt;🚀 Critical Integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Fragility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🪨 Resilient&lt;/td&gt;
&lt;td&gt;🍌 More Fragile&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Speed&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🚤 Fast&lt;/td&gt;
&lt;td&gt;⛵ Slower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;On Failure&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;🎯 Easy to pinpoint&lt;/td&gt;
&lt;td&gt;👓 Potentially harder&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In summary, Unit Tests target small, isolated units of code without external dependencies, while Integration Tests examine the interactions and interfaces between components or services.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do these Relate to Angular Component Testing?
&lt;/h2&gt;

&lt;p&gt;I think this where the lines blur between what [I would consider] a unit vs an integration test is. An Angular Component consists of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;An HTML template that declares what renders on the page&lt;/li&gt;
&lt;li&gt;A TypeScript class that defines behavior [sic]&lt;/li&gt;
&lt;li&gt;A CSS selector that defines how the component is used in a template&lt;/li&gt;
&lt;li&gt;Optionally, CSS styles applied to the template&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;-- &lt;cite&gt;&lt;a href="https://angular.io/guide/component-overview#angular-components-overview"&gt;Angular Guide - Angular components overview&lt;/a&gt;&lt;cite&gt;&lt;/cite&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I ask you this question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What is &lt;em&gt;'Isolation'&lt;/em&gt; or the &lt;em&gt;Unit Under Test&lt;/em&gt; in the Context of an Angular Component?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;I think if your test focuses on the &lt;strong&gt;class&lt;/strong&gt; of a single Angular component in isolation, it's more likely to be a unit test.&lt;/li&gt;
&lt;li&gt;If your test involves &lt;strong&gt;rendering multiple Angular components together&lt;/strong&gt;, in a more real-world scenario, and / or &lt;strong&gt;interacting with the DOM&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;/strong&gt;, I think it is probably an Integration Test&lt;sup id="fnref2"&gt;2&lt;/sup&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  An example
&lt;/h2&gt;

&lt;p&gt;For example, testing the click of a button on a component:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;component.onClick();&lt;/code&gt; (assuming this is what the &lt;code&gt;onclick&lt;/code&gt; event handler calls)

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type of Test?&lt;/strong&gt; 🧩 Unit Test&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why?&lt;/strong&gt; Interacting directly with the public interface of the class / unit under test.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fixture.nativeElement.querySelector('button').click();&lt;/code&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Type of Test?&lt;/strong&gt; 🤝 Integration Test&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Why?&lt;/strong&gt; The test interacts with the DOM, so likely needs to render other dependencies and components. One could argue the DOM itself is a dependency&lt;sup id="fnref3"&gt;3&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;But that's not an Integration Test, it's a Component DOM Test!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You're spot on. If you read the &lt;a href="https://angular.io/guide/testing-components-basics"&gt;Angular Guide for testing Components&lt;/a&gt;, the docs call the tests 'Component Class Tests' and 'Component DOM Tests', no where does it mention unit testing&lt;sup id="fnref4"&gt;4&lt;/sup&gt;. Perhaps the terminology we use day-to-day is wrong? Either way I think this leads to a grey area of what we refer to as 'unit testing' Angular Components. Moreover, here is further definition of an Angular Component:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;...a component is more than just its class. A component interacts with the DOM and with other components. The class-only tests can tell you about class behavior [sic]. They cannot tell you if the component is going to render properly, respond to user input and gestures, or integrate with its parent and child components.&lt;/p&gt;

&lt;p&gt;-- &lt;cite&gt;&lt;a href="https://angular.io/guide/testing-components-basics#component-dom-testing"&gt;Angular Guide - Component DOM testing&lt;/a&gt;&lt;/cite&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So if Component DOM Tests are to address this 'limitation', that must mean the Component DOM Tests need to have dependencies and 'integrate with its parent and child components'? Sounds pretty close to the definition of an Integration Test to me.&lt;/p&gt;

&lt;p&gt;I felt there had to be a cleaner, more succinct way to draw the line between Component Unit Tests vs Component DOM Tests vs Integration Tests. This is where I went back to my books&lt;sup id="fnref5"&gt;5&lt;/sup&gt; and considered Sociable and Solitary tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;In the next post, we'll explore more about Sociable and Solitary testing and why this concept could be a solution to the 'grey area', and how we describe and talk about Angular Component testing.&lt;/p&gt;




&lt;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://github.com/jsdom/jsdom"&gt;JSDOM&lt;/a&gt; is a 'real' DOM: ↩&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;jsdom is a pure-JavaScript implementation of many web standards, notably the WHATWG DOM...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;The Angular docs call these &lt;a href="https://angular.io/guide/testing-components-basics#component-dom-testing"&gt;Component DOM testing&lt;/a&gt;. I think they are just another type of Integration Test, more on that later. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;I suspect if you were to run &lt;a href="https://jestjs.io/docs/configuration#testenvironment-string"&gt;Jest without JSDOM&lt;/a&gt;, this sort of interaction would fail. ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;On the &lt;a href="https://angular.io/guide/testing-components-basics"&gt;Basics of testing components&lt;/a&gt; page anyway, and I cannot find anywhere where the Angular framework / team makes the link between Component tests (class or DOM) and unit tests. However, in the documentation for &lt;code&gt;ng test&lt;/code&gt;, Angular describes command as "Runs unit tests...". ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;&lt;a href="https://www.artofunittesting.com/"&gt;'The Art of Unit Testing' by Roy Osherove&lt;/a&gt; ↩&lt;/p&gt;

&lt;p&gt;&lt;a href="https://leanpub.com/wewut"&gt;'Working Effectively with Unit Tests' by Jay Fields&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.oreilly.com/library/view/test-driven-development/0321146530/"&gt;'Test-Driven Development By Example' by Kent Beck&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>angular</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
