<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Charles Desneuf</title>
  <subtitle>Charles Desneuf&#39;s blog about software engineering, testing, refactoring, and craftsmanship. Practical insights for building better software.</subtitle>
  <link href="https://blog.charlesdesneuf.com/feed.xml" rel="self"/>
  <link href="https://blog.charlesdesneuf.com/"/>
  
    <updated>2026-02-01T00:00:00Z</updated>
  
  <id>https://blog.charlesdesneuf.com</id>
  <author>
    <name>Charles Desneuf</name>
    
  </author>
  
    
    <entry>
      <title>Using PrismJs with AngularJs</title>
      <link href="https://blog.charlesdesneuf.com/articles/prismjs-and-angularjs/"/>
      <updated>2013-10-18T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/prismjs-and-angularjs/</id>
      <summary>If you need to display code snippets in an AngularJs application you might want to read this !</summary>
      <content type="html">&lt;p&gt;In a project I’m working on I need to display code snippets. I found &lt;a href=&quot;http://prismjs.com/&quot;&gt;PrismJs&lt;/a&gt; library, which is lightweight and cover a lot of languages. The application is based on AngularJs, and code snippets are displayed after having been read from an API and PrimsJs is run before code is injected in the template, when used as the documentation said, so highlighting doesn’t work. The problem can be tackled down with the creation of a directive. Here is the code :&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;angular&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Prism&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;directive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;prism&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token literal-property property&quot;&gt;restrict&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;A&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token function-variable function&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attrs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                element&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ready&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                    Prism&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;highlightElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;element&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For each HTML element marked with the prism attribute we wait that the inner DOM is fully loaded, using the ready function, and then trigger Prism highlighting on it.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;pre&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;code&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;language-markup&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;prism&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    {{ snippet }}&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;code&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;pre&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is the &lt;a href=&quot;https://gist.github.com/SelrahcD/7042692&quot;&gt;Gist&lt;/a&gt; !&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>In modal nested states with AngularJs ui-router</title>
      <link href="https://blog.charlesdesneuf.com/articles/angularsjs-ui-router-and-in-modal-nested-states/"/>
      <updated>2014-01-15T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/angularsjs-ui-router-and-in-modal-nested-states/</id>
      <summary>Create nested states in a modal using ui-router for your AngularJs application</summary>
      <content type="html">&lt;p&gt;In an AngularJs application I&#39;m working on we need to display details about an object on a modal and to be able to switch to an edit mode in the same modal.&lt;br&gt;
An other requirement is that the modal is automatically opened when the user enters the application with a url pointing to an edit or a view mode.&lt;/p&gt;
&lt;p&gt;We are using the modal system made by &lt;a href=&quot;http://angular-ui.github.io/bootstrap/&quot;&gt;Ui Bootstrap&lt;/a&gt; and the &lt;a href=&quot;https://github.com/angular-ui/ui-router&quot;&gt;Ui Router&lt;/a&gt; both created by the AngularUi team.&lt;/p&gt;
&lt;p&gt;Ui Router allows to organize the interface using a state machine.&lt;/p&gt;
&lt;p&gt;As described in the introduction we need at least three states. The first one shows a list of items, and the two others are our modal&#39;s states (view and edition of the item).&lt;/p&gt;
&lt;p&gt;We also don&#39;t want the modal to close and open when switching between view and edition mode. Here helps a really cool feature of Ui Router : &lt;a href=&quot;https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views#abstract-states&quot;&gt;abstract states&lt;/a&gt;. An abstract state is a state that cannot be activated itself but can have child states. We use an abstract state to deal with the modal opening, view and edit mode are its child states.&lt;/p&gt;
&lt;p&gt;Here is a plunker of the result :&lt;/p&gt;
&lt;iframe style=&quot;width:100%; height:500px;&quot; src=&quot;https://embed.plnkr.co/5Mmu4w&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;In our application config we define a state for the list of items:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;$stateProvider&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;ul&gt;&amp;lt;li ng-repeat=&quot;pony in ponies&quot;&gt;&amp;lt;a ui-sref=&quot;view({id: pony.id})&quot;&gt;&amp;lt;/a&gt;&amp;lt;/li&gt;&amp;lt;/ul&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function-variable function&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      $scope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ponies &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ponies&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We add the abstract state :&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;$stateProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;modal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;abstract&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;onEnter&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;$modal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;$state&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$modal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Open modal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;      $modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;div ui-view=&quot;modal&quot;&gt;&amp;lt;/div&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;backdrop&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;windowClass&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;right fade&#39;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        $state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;go&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The abstract state is a child of the list state and therefore can be activated at the same time of the list. When this state is activated a modal is opened and it contains a basic template with an other ui-view name modal. We also use $modal promise system to go back to the list state when the modal is closed.&lt;/p&gt;
&lt;p&gt;We finally add the edit and view states :&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;$stateProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;view&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;:id&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;modal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string-property property&quot;&gt;&#39;modal@&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;h1&gt;&amp;lt;/h1&gt;&amp;lt;br /&gt;&#92;&lt;br&gt;      &amp;lt;a ui-sref=&quot;edit({id: pony.id})&quot;&gt;Edit&amp;lt;/a&gt;&amp;lt;br /&gt;&#92;&lt;br&gt;      &amp;lt;a href=&quot;#&quot; ng-click=&quot;$close()&quot;&gt;Close&amp;lt;/a&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token function-variable function&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pony&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        $scope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pony &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pony&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token function-variable function&quot;&gt;pony&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$stateParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ponies&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;$stateParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;edit&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;:id/edit&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;modal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token literal-property property&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token string-property property&quot;&gt;&#39;modal@&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;h1&gt;Edit &amp;lt;/h1&gt;&amp;lt;br /&gt; &#92;&lt;br&gt;        &amp;lt;a ui-sref=&quot;view({id: pony.id})&quot;&gt;View&amp;lt;/a&gt; &amp;lt;br /&gt;&#92;&lt;br&gt;        &amp;lt;a href=&quot;#&quot; ng-click=&quot;$close()&quot;&gt;Close&amp;lt;/a&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token function-variable function&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pony&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        $scope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pony &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pony&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token function-variable function&quot;&gt;pony&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$stateParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;          &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ponies&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;$stateParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They both inherit from the modal state and set the content of the modal view.&lt;br&gt;
We use resolve to load the needed item accordingly to the url parameter, this allows the use to reach the application with an url such as /2/edit and to see both the items list and the modal open on edition mode. Cool huh ?&lt;/p&gt;
&lt;p&gt;Here is the full sample of code which allows you to have two states in the same modal :&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;angular&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;app&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;ui.router&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;ui.bootstrap&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;$stateProvider&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;$urlRouterProvider&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$stateProvider&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $urlRouterProvider&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;br&gt;    $urlRouterProvider&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;otherwise&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; ponies &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Griotte&#39;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Eole&#39;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Rox&#39;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;br&gt;    $stateProvider&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;ul&gt;&amp;lt;li ng-repeat=&quot;pony in ponies&quot;&gt;&amp;lt;a ui-sref=&quot;view({id: pony.id})&quot;&gt;&amp;lt;/a&gt;&amp;lt;/li&gt;&amp;lt;/ul&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token function-variable function&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;          $scope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ponies &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ponies&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;modal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;abstract&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;onEnter&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;$modal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;$state&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$modal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; $state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Open modal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            $modal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;              &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;div ui-view=&quot;modal&quot;&gt;&amp;lt;/div&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;              &lt;span class=&quot;token literal-property property&quot;&gt;backdrop&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;              &lt;span class=&quot;token literal-property property&quot;&gt;windowClass&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;right fade&#39;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;finally&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;              $state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;go&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;list&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;view&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;:id&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;modal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;          &lt;span class=&quot;token string-property property&quot;&gt;&#39;modal@&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;h1&gt;&amp;lt;/h1&gt;&amp;lt;br /&gt;&#92;&lt;br&gt;            &amp;lt;a ui-sref=&quot;edit({id: pony.id})&quot;&gt;Edit&amp;lt;/a&gt;&amp;lt;br /&gt;&#92;&lt;br&gt;            &amp;lt;a href=&quot;#&quot; ng-click=&quot;$close()&quot;&gt;Close&amp;lt;/a&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token function-variable function&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pony&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;              $scope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pony &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pony&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token literal-property property&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;              &lt;span class=&quot;token function-variable function&quot;&gt;pony&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$stateParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ponies&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;$stateParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;edit&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;:id/edit&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;modal&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;          &lt;span class=&quot;token string-property property&quot;&gt;&#39;modal@&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token literal-property property&quot;&gt;template&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&amp;lt;h1&gt;Edit &amp;lt;/h1&gt;&amp;lt;br /&gt; &#92;&lt;br&gt;              &amp;lt;a ui-sref=&quot;view({id: pony.id})&quot;&gt;View&amp;lt;/a&gt; &amp;lt;br /&gt;&#92;&lt;br&gt;              &amp;lt;a href=&quot;#&quot; ng-click=&quot;$close()&quot;&gt;Close&amp;lt;/a&gt;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token function-variable function&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pony&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;              $scope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pony &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; pony&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token literal-property property&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;              &lt;span class=&quot;token function-variable function&quot;&gt;pony&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$stateParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; ponies&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;$stateParams&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Add more element forms the nice way with AngularJs</title>
      <link href="https://blog.charlesdesneuf.com/articles/add-more-form-style-the-nice-way-with-angularjs/"/>
      <updated>2014-02-15T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/add-more-form-style-the-nice-way-with-angularjs/</id>
      <summary>Build a form with the classic add more element button in a cool way with AngularJs.</summary>
      <content type="html">&lt;p&gt;We sometimes have to deal with forms where user can add items to a list. Most of the time an &amp;quot;Add more&amp;quot; button is created and we add an input to the form when it&#39;s clicked. I&#39;ll show you in this post how to build this type of form using AngularJs form handling mechanism.&lt;/p&gt;
&lt;p&gt;Here is the result of what will be discussed on this post, with some more things like CSS and placeholders :&lt;/p&gt;
&lt;iframe style=&quot;width:100%; height:450px;&quot; src=&quot;https://embed.plnkr.co/LKlkaaenbf9OeJrmXVES/preview&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;As you can see the &amp;quot;Add an element&amp;quot; button is disabled if an item of the list is invalid (they are all required) and the &amp;quot;Save list&amp;quot; is disabled if one element of the form is invalid.&lt;/p&gt;
&lt;p&gt;This example makes a deep use of the &lt;a href=&quot;http://docs.angularjs.org/api/ng.directive:form&quot;&gt;form directive&lt;/a&gt;. AngularJs allows to interlock form directives. Each directive will regiter itself upon the one above in the DOM tree. Using a form directive is helpful when you have to check the validity of several inputs without having to do a combination of all inputs states.&lt;/p&gt;
&lt;p&gt;We will use the &amp;quot;ng-form&amp;quot; version of the directive because form tag can&#39;t contain an other form tag.&lt;/p&gt;
&lt;p&gt;First create a basic form for the list title. The submit button is disabled when the form is not valid. The form tag is one of the way to create a form directive.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;listForm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-submit&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;saveList()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;novalidate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;list-title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;List title&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;list-title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-model&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;list.title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Save list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-disabled&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;listForm.$invalid&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use ng-repeat for the item inputs. Here two more forms are created using the ng-form attribute : one for the list of element (itemsForm) and one for each element (itemForm). We are now able to do validation for one item and for the list. User can&#39;t add an item to the list if one of the existing elements is invlaid because the &amp;quot;Add an element&amp;quot; button is disabled when itemsForm is not valid.&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;form&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;listForm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-submit&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;saveList()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;novalidate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;list-title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;List title&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;list-title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-model&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;list.title&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;List items&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-form&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;itemsForm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-repeat&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;item in list.items&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-form&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;itemForm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;itemText&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;List item&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;itemText&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-model&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;item.text&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;required&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ul&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-show&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;itemForm.$invalid &amp;amp;&amp;amp; itemForm.$dirty&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-show&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;itemForm.itemText.$error.required&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;This field is required.&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;li&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;ul&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;addElement()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-disabled&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;itemsForm.$invalid&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Add an element&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Save list&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-disabled&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;listForm.$invalid&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;form&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code of the associated controller is easy to understand : when the addElement function is called a new empty object is pushed in the array of items and a new row will be displayed in the form.&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;angular&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;formDemo&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;FormCtrl&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;$scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    $scope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token literal-property property&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    $scope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;addElement&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        $scope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;items&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    $scope&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;saveList&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token function&quot;&gt;alert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;This is a dummy button but let&#92;&#39;s say the list has been saved !&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;    &lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you have a question or want to discuss about this solution you can find me on &lt;a href=&quot;https://twitter.com/Selrahcd&quot;&gt;Twitter&lt;/a&gt; or comment this post.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Smurfing a Twitter bot</title>
      <link href="https://blog.charlesdesneuf.com/articles/smurfing-a-bot/"/>
      <updated>2015-02-10T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/smurfing-a-bot/</id>
      <summary>How I made a bot that retweet a French newspaper Twitter feed as a Smurf</summary>
      <content type="html">&lt;h2 id=&quot;the-idea&quot; tabindex=&quot;-1&quot;&gt;The idea&lt;/h2&gt;
&lt;p&gt;The idea came up a while ago when I was talking with colleagues : Would it be possible to make a bot able to retweet speaking as a Smurf ?&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/The_Smurfs&quot;&gt;Smurfs&lt;/a&gt;, or &lt;a href=&quot;http://fr.wikipedia.org/wiki/Les_Schtroumpfs&quot;&gt;Schtroumpfs&lt;/a&gt; in french, are an imaginary population of small blue people living in a mushroom village. They were born in the imagination of french illustrator Peyo. They speak a strange language, which is based on human language but where they replace some words with smurf.&lt;/p&gt;
&lt;p&gt;I&#39;m a follower of french newspaper &lt;a href=&quot;http://www.lemonde.fr/&quot;&gt;Le Monde&lt;/a&gt; on Twitter (&lt;a href=&quot;https://twitter.com/lemondefr&quot;&gt;@lemondefr&lt;/a&gt;) and I decided that&lt;br&gt;
it would be a good test to try to smurf their tweets.&lt;/p&gt;
&lt;h2 id=&quot;language-analysis&quot; tabindex=&quot;-1&quot;&gt;Language analysis&lt;/h2&gt;
&lt;p&gt;The analysis of sentences is something complicated and might be harder in french than in some other language. I didn&#39;t even imagine for a second I could get along with this project without a good parser.&lt;/p&gt;
&lt;p&gt;I searched the web for a few hours and finally discovered the work done by &lt;a href=&quot;https://www.rocq.inria.fr/alpage-wiki/tiki-index.php?page=Accueil&quot;&gt;Alpage&lt;/a&gt;, a research group working at INRIA, the french computer science search institute.&lt;br&gt;
They have created a set of tools that can analyse a sentence and returns &lt;a href=&quot;http://en.wikipedia.org/wiki/Lexeme&quot;&gt;lexems&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can &lt;a href=&quot;http://alpage.inria.fr/frmgwiki/frmg_main/frmg_server&quot;&gt;try it by yourself&lt;/a&gt; if you can speak a few words of french.&lt;/p&gt;
&lt;p&gt;I tried to install the Alpage system but failed to do so and finally decided to make a call to a demo page, get the result and work with the received data.&lt;/p&gt;
&lt;h2 id=&quot;word-transformation&quot; tabindex=&quot;-1&quot;&gt;Word transformation&lt;/h2&gt;
&lt;p&gt;Once the sentence is parsed into lexems the next step is to replace word by its smurf equivalent. Smurf language is not just about changing a word to smurf: you have to replace it with the smurf word in the same category.&lt;/p&gt;
&lt;figure&gt;
    &lt;table&gt;
        &lt;tr&gt;
            &lt;th&gt;Category&lt;/th&gt;
            &lt;th&gt;Smurf form&lt;/th&gt;
            &lt;th&gt;French word example&lt;/th&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Adverb&lt;/td&gt;
            &lt;td&gt;schtroupfement&lt;/td&gt;
            &lt;td&gt;admirablement&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Past verb, plural form&lt;/td&gt;
            &lt;td&gt;schtroumpfaient&lt;/td&gt;
            &lt;td&gt;venaient&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Noun, plural form&lt;/td&gt;
            &lt;td&gt;schtroumpfs&lt;/td&gt;
            &lt;td&gt;poneys&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/table&gt;
&lt;figcaption&gt;Examples of french-smurf translations&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I wrote a basic french-smurf &lt;a href=&quot;https://github.com/SelrahcD/leschtroumpffr/blob/master/language.js&quot;&gt;translation dictionnary&lt;/a&gt; to help me with the transformation.&lt;/p&gt;
&lt;p&gt;Doing the transformation of one word is not enough. I also had to transform previous words in some cases where gender and number influence the preceding pronoun.&lt;/p&gt;
&lt;figure&gt;
    &lt;table&gt;
        &lt;tr&gt;
            &lt;th&gt;Smurf form&lt;/th&gt;
            &lt;th&gt;French example&lt;/th&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Je schtroumpfe&lt;/td&gt;
            &lt;td&gt;J&#39;arrive&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
            &lt;td&gt;Du schtroumpf&lt;/td&gt;
            &lt;td&gt;De l&#39;eau&lt;/td&gt;
        &lt;/tr&gt;
    &lt;/table&gt;
&lt;figcaption&gt;Example of influence of gender and number on the translation&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I didn&#39;t try to be clever in the selection of the french word to transform : this is a total random thing.&lt;/p&gt;
&lt;h2 id=&quot;integration-with-twitter&quot; tabindex=&quot;-1&quot;&gt;Integration with Twitter&lt;/h2&gt;
&lt;p&gt;The project is written in NodeJS and &lt;a href=&quot;https://github.com/ttezel/twit&quot;&gt;Twit&lt;/a&gt; does a good job when it comes to listen Twitter&#39;s stream API and to post a tweet so I didn&#39;t search for too long and decided to go with it.&lt;/p&gt;
&lt;p&gt;The bot listen the stream API for each tweet from &lt;a href=&quot;https://twitter.com/lemondefr&quot;&gt;@lemondefr&lt;/a&gt; and delegate the transformation work to the &lt;a href=&quot;https://github.com/SelrahcD/leschtroumpffr/blob/master/schtroumpsify.js&quot;&gt;transformation tool&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once the text transformed the bot makes the call to tweet the smurfed version of the text.&lt;/p&gt;
&lt;h2 id=&quot;release&quot; tabindex=&quot;-1&quot;&gt;Release&lt;/h2&gt;
&lt;p&gt;As always I had a hard time convincing myself I should release an uncompleted project, with some known bugs and possible improvements, but I finally did it.&lt;br&gt;
The bot is hosted on a DigitalOcean droplet.&lt;/p&gt;
&lt;p&gt;Some bugs are persisting and I have to manually restart the bot once in a while. I also make some changes in the transformation algorithm when a tweet is not doing so well.&lt;/p&gt;
&lt;h2 id=&quot;results&quot; tabindex=&quot;-1&quot;&gt;Results&lt;/h2&gt;
&lt;p&gt;The bot is living is own life &lt;a href=&quot;https://twitter.com/leschtroumpffr&quot;&gt;out there&lt;/a&gt;. Go and follow it !&lt;/p&gt;
&lt;p&gt;As the transformed word is selected randomly luck plays a good part in the quality of the generated tweet.&lt;/p&gt;
&lt;p&gt;The bot is able to do some cool sentences such as :&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet tw-align-center&quot; data-cards=&quot;hidden&quot; lang=&quot;fr&quot;&gt;
  &lt;p&gt;Droit de vote des schtroumpfs : Londres condamné à Strasbourg &lt;a href=&quot;http://t.co/0mV5ahnxoK&quot;&gt;http://t.co/0mV5ahnxoK&lt;/a&gt;&lt;/p&gt;
  &amp;mdash; Le Schtroumpf (@leschtroumpffr) &lt;a href=&quot;https://twitter.com/leschtroumpffr/status/565125702379651072&quot;&gt;10 Février 2015&lt;/a&gt;
&lt;/blockquote&gt;
&lt;blockquote class=&quot;twitter-tweet tw-align-center&quot; lang=&quot;fr&quot;&gt;&lt;p&gt;Les experts divisés sur un schtroumpf attribué à Léonard de Vinci saisi en Suisse &lt;a href=&quot;http://t.co/waHvCuE90i&quot;&gt;http://t.co/waHvCuE90i&lt;/a&gt;&lt;/p&gt;&amp;mdash; Le Schtroumpf (@leschtroumpffr) &lt;a href=&quot;https://twitter.com/leschtroumpffr/status/565197113806831616&quot;&gt;10 Février 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;blockquote class=&quot;twitter-tweet tw-align-center&quot; data-cards=&quot;hidden&quot; lang=&quot;fr&quot;&gt;&lt;p&gt;Harvard interdit les relations sexuelles entre professeurs et schtroumpfs &lt;a href=&quot;http://t.co/E1ELllkpzB&quot;&gt;http://t.co/E1ELllkpzB&lt;/a&gt; sur &lt;a href=&quot;https://twitter.com/Campus_LeMonde&quot;&gt;@Campus_LeMonde&lt;/a&gt; &lt;a href=&quot;http://t.co/3nVVjIVJAE&quot;&gt;pic.twitter.com/3nVVjIVJAE&lt;/a&gt;&lt;/p&gt;&amp;mdash; Le Schtroumpf (@leschtroumpffr) &lt;a href=&quot;https://twitter.com/leschtroumpffr/status/564789667116359680&quot;&gt;9 Février 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;Sometimes we are less lucky :&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet tw-align-center&quot; data-cards=&quot;hidden&quot; lang=&quot;fr&quot;&gt;&lt;p&gt;Spider-Man rejoint l&amp;#39;univers schtroumpf de Marvel &lt;a href=&quot;http://t.co/FP4Xd40mlo&quot;&gt;http://t.co/FP4Xd40mlo&lt;/a&gt;&lt;/p&gt;&amp;mdash; Le Schtroumpf (@leschtroumpffr) &lt;a href=&quot;https://twitter.com/leschtroumpffr/status/565106823876055041&quot;&gt;10 Février 2015&lt;/a&gt;&lt;/blockquote&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;h2 id=&quot;see-the-code&quot; tabindex=&quot;-1&quot;&gt;See the code&lt;/h2&gt;
&lt;p&gt;The code is freely available &lt;a href=&quot;https://github.com/SelrahcD/leschtroumpffr&quot;&gt;on github&lt;/a&gt;.&lt;br&gt;
Let&#39;s say it upfront : it&#39;s really not a good piece of software but the job is done.&lt;/p&gt;
&lt;p&gt;Hey ! I&#39;m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; too, if you want to chat about the bot or something else. Feel free to comment below as well.&lt;/p&gt;
&lt;script async src=&quot;//platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
</content>
    </entry>
  
    
    <entry>
      <title>TShirt a day</title>
      <link href="https://blog.charlesdesneuf.com/articles/tshirt-a-day-serie/"/>
      <updated>2015-07-06T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/tshirt-a-day-serie/</id>
      <summary>Code, learning and t-shirts</summary>
      <content type="html">&lt;p&gt;I’ve been reading a lot of books and blog posts, watching a lot of conferences about development for the past year and I was searching a project to help me wrap my head around all the interesting concepts I’ve learned.&lt;/p&gt;
&lt;p&gt;Some day I received another t-shirt from a &lt;a href=&quot;https://ninaschool.bandcamp.com/&quot;&gt;punk rock band&lt;/a&gt; - I have so many t-shirts... - and had an idea : &amp;quot;What if I made a service where people could choose what t-shirt I should wear on a given day ?&amp;quot;&lt;/p&gt;
&lt;p&gt;This is stupid but can be a decent project to play with &lt;a href=&quot;https://en.wikipedia.org/wiki/Domain-driven_design&quot;&gt;Domain Driven Design&lt;/a&gt; concepts, to build a true &lt;a href=&quot;https://fr.wikipedia.org/wiki/Representational_State_Transfer&quot;&gt;Restfull API&lt;/a&gt; (ouch...), to work on some front-end again, to (maybe) try to use &lt;a href=&quot;https://www.docker.com/&quot;&gt;docker&lt;/a&gt; as a container, to do some &lt;a href=&quot;https://en.wikipedia.org/wiki/Behavior-driven_development&quot;&gt;Behaviour Driven Design&lt;/a&gt;, to practice my unit test writing skills...&lt;/p&gt;
&lt;p&gt;If this project goes to production some day it could lead to a funny experimentation too because I have some t-shirts I probably should be embarassed to wear in some contexts.&lt;/p&gt;
&lt;p&gt;(And if this goes to production this might lead to learn some more devops stuff. They say the more you do devops stuff the more you like doing it.)&lt;/p&gt;
&lt;p&gt;This is project is open sourced, you can &lt;a href=&quot;https://github.com/SelrahcD/tshirtaday&quot;&gt;find it on Github&lt;/a&gt; and I’ll do my best to write some blog posts with the thing I’ve learned and struggle with.&lt;/p&gt;
&lt;p&gt;Here are all the blog posts in the Tshirt a day serie :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/articles/bdd-and-domain-code/&quot;&gt;BDD and domain code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/articles/project-macro-structure/&quot;&gt;Project macro structure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/articles/refactoring-votevalidator-with-tests/&quot;&gt;Refactoring VoteValidator with tests&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hey ! I’m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; too, if you want to chat about this serie or something else. Feel free to comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>BDD and domain code</title>
      <link href="https://blog.charlesdesneuf.com/articles/bdd-and-domain-code/"/>
      <updated>2015-07-06T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/bdd-and-domain-code/</id>
      <summary>How to use Behat as a design tool.</summary>
      <content type="html">&lt;p&gt;&lt;a href=&quot;http://docs.behat.org/en/v2.5/&quot;&gt;Behat&lt;/a&gt; is a well known tool for end-to-end testing but as &lt;a href=&quot;https://twitter.com/everzet&quot;&gt;Konstantin Kudryashov&lt;/a&gt; pointed out in his article &lt;a href=&quot;http://everzet.com/post/99045129766/introducing-modelling-by-example&quot;&gt;Introducing Modelling by Example&lt;/a&gt; it can be used as a modeling tool too.&lt;/p&gt;
&lt;p&gt;As Konstantin points out writing features using an &lt;a href=&quot;http://martinfowler.com/bliki/UbiquitousLanguage.html&quot;&gt;ubiquitous language&lt;/a&gt; shared by developers and stackholders reduce the cost of translation and thus the number of errors in the application.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;/articles/tshirt-a-day-serie/&quot;&gt;t-shirt a day project&lt;/a&gt; one of the feature is that an admin can manage a t-shirt catalog. This feature is described using Gherkin language and scenario are tested using Behat.&lt;/p&gt;
&lt;pre class=&quot;language-gherkin&quot;&gt;&lt;code class=&quot;language-gherkin&quot;&gt;&lt;span class=&quot;token feature&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;Feature:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    As an admin&lt;br&gt;    &lt;span class=&quot;token atrule&quot;&gt;I&lt;/span&gt; want to manage the TShirt Catalog&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token scenario&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;Scenario:&lt;/span&gt;&lt;span class=&quot;token important&quot;&gt; Adding a TShirt to the catalog&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atrule&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;a&lt;/span&gt; new catalog is created&lt;br&gt;    &lt;span class=&quot;token atrule&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;an&lt;/span&gt; admin adds a TShirt with description &lt;span class=&quot;token string&quot;&gt;&quot;La Ruda Japan Tour&quot;&lt;/span&gt; to the catalog&lt;br&gt;    &lt;span class=&quot;token atrule&quot;&gt;Then&lt;/span&gt; the catalog should contain 1 TShirt&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token scenario&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;Scenario:&lt;/span&gt;&lt;span class=&quot;token important&quot;&gt; Removing a TShirt to the catalog&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atrule&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;a&lt;/span&gt; new catalog is created&lt;br&gt;    &lt;span class=&quot;token atrule&quot;&gt;Given&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;a&lt;/span&gt; TShirt with id 12345 is added to the catalog&lt;br&gt;    &lt;span class=&quot;token atrule&quot;&gt;When&lt;/span&gt; &lt;span class=&quot;token atrule&quot;&gt;an&lt;/span&gt; admin removes the TShirt with id 12345 from the catalog&lt;br&gt;    &lt;span class=&quot;token atrule&quot;&gt;Then&lt;/span&gt; the catalog should contain 0 TShirt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see scenarios are written using terms coming from the business and say nothing about the implementation, developers and stackholders are sharing the same language which remove one layer of translation.&lt;/p&gt;
&lt;p&gt;The second layer of translation, from a language understood by developers to code, can be reduced using the DDD advice to use the ubiquitous language in code too. As you’ll see below the code expresses the feature requirements in a way that should allow non developers to read it with a little help.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;ManageCatalogContext&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; SnippetAcceptingContext&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$catalog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @Given a new catalog is created&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;aNewCatalogIsCreated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;catalog&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InMemoryCatalog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @When an admin adds a TShirt with description :description to the catalog&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;anAdminAddsATshirtWithDescriptionToTheCatalog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$admin&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Admin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$admin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addTShirtWithDescriptionToCatalog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;catalog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @Then the catalog should contain :count TShirt&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;theCatalogShouldContainTshirt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token class-name class-name-fully-qualified static-context&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;PHPUnit_Framework_Assert&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;catalog&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @Given a TShirt with id :id is added to the catalog&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;aTshirtWithIdIsAddedToTheCatalog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;catalog&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TShirt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TShirtId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Dummy description&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;   &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @When an admin removes the TShirt with id :id from the catalog&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;anAdminRemovesTheTshirtWithIdFromTheCatalog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$admin&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Admin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$admin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;removeTShirtWithIdFromCatalog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TShirtId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;catalog&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the cost of translation has been reduced a lot using this approach.&lt;/p&gt;
&lt;p&gt;As an aside I have to admit that I have some trouble writing user stories and the article &lt;a href=&quot;http://dannorth.net/whats-in-a-story/&quot;&gt;What’s in a story&lt;/a&gt; helped me a lot and is a very good reference.&lt;/p&gt;
&lt;p&gt;Hey ! I’m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; too, if you want to chat about the bot or something else. Feel free to comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Project macro structure</title>
      <link href="https://blog.charlesdesneuf.com/articles/project-macro-structure/"/>
      <updated>2015-07-12T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/project-macro-structure/</id>
      <summary>Thoughts about the file organization of a project at the macro level</summary>
      <content type="html">&lt;p&gt;File organization is something crucial if you have to maintain a project for the long run but is often overlooked by developers.&lt;/p&gt;
&lt;p&gt;The way files are organized within a project is very important because it dictates how the team will work and at the end the overall quality of the software.&lt;/p&gt;
&lt;p&gt;Using the traditional framework organization with a directory for each type of element (entities, services, controllers, ...) is one of the fastest way to produce tightly coupled pieces of software because all developers are working in the same area and have no boundaries to help them structure their code.&lt;/p&gt;
&lt;p&gt;Furthermore this file organization doesn’t convey any information about what the software does. As Robert C. Martin noted in his conference &lt;a href=&quot;https://www.youtube.com/watch?v=WpkDN78P884&quot;&gt;&amp;quot;Architecture the lost years&amp;quot;&lt;/a&gt; we should be able to identify the purpose of a project by looking at its structure at the macro level.&lt;/p&gt;
&lt;p&gt;Providing a file structure which expresses the goal of the software helps a lot in the definition of boundaries inside the project. This boundaries can be used to determine the teams working on the project too. Having boundaries is also the starting point to the analyze of dependencies within the system.&lt;/p&gt;
&lt;p&gt;In the &lt;a href=&quot;/articles/tshirt-a-day-serie/&quot;&gt;t-shirt a day project&lt;/a&gt; I have tried two files structure with the intent to give information about what the software does but I was unhappy with the first one that was not expressive enough in my opinion.&lt;/p&gt;
&lt;figure&gt;
    &lt;pre class=&quot;filestructure&quot;&gt;
    .
    ├── README.md
    ├── behat.yml
    ├── bin
    ├── composer.json
    ├── composer.lock
    ├── phpunit.xml
    ├── src
    │   └── tshirtaday
    │       ├── catalog
    │       │   ├── features
    │       │   ├── src
    │       │   └── tests
    │       └── votes
    │           ├── features
    │           ├── src
    │           └── tests
    └── vendor
    &lt;/pre&gt;
&lt;figcaption&gt;First file structure&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;In this first version I focused to much in my attempt to segregate domain code from a potential framework. I made a directory to support all domain code containing the two current modules (votes and catalog). This two modules were also containing their behat’s feature file.&lt;br&gt;
The main issue with this approach is that you cannot tell what the system is about at first glance.&lt;/p&gt;
&lt;figure&gt;
    &lt;pre class=&quot;filestructure&quot;&gt;
    .
    ├── README.md
    ├── behat.yml
    ├── bin
    ├── catalog
    │   ├── contexts
    │   ├── src
    │   └── tests
    ├── composer.json
    ├── composer.lock
    ├── features
    ├── phpunit.xml
    ├── vendor
    └── votes
        ├── contexts
        ├── src
        └── tests
    &lt;/pre&gt;
&lt;figcaption&gt;Second file structure&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After refactoring the file structure the two modules are now placed at the top level, which is a huge gain on the expressiveness  of the system. Moreover the features&#39; directory is also situated at the top level because they give a good description of what the entire system is about. I’m also trying to follow the example of Konstantin Kudryashov with is article &lt;a href=&quot;http://everzet.com/post/99045129766/introducing-modelling-by-example&quot;&gt;Modeling by example&lt;/a&gt; and doing so might allow a feature to be accomplished by several modules altogether. For instance if I later introduce an API on top of the two modules I will need both the API module and the two others to interact to do the job.&lt;/p&gt;
&lt;p&gt;This little refactoring helps showing the intent of the project. I’m currently happy with it and will see what will be the consequences of this decision during the lifetime of the project.&lt;/p&gt;
&lt;p&gt;Hey ! I’m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; too, if you want to chat about this approach or something else. Feel free to comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Why are 404 pages still a thing ?</title>
      <link href="https://blog.charlesdesneuf.com/articles/why-are-404-pages-still-a-thing/"/>
      <updated>2015-07-20T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/why-are-404-pages-still-a-thing/</id>
      <summary>Caches, 404 and missed business oportunities</summary>
      <content type="html">&lt;p&gt;In this article every time you’ll see &amp;quot;a 404 page&amp;quot; please read &amp;quot;The dumb 404 page with an apology and a link to the home page&amp;quot;. This doesn’t concern 404 in other contexts, please keep - or start - using them in you APIs, for instance.&lt;/p&gt;
&lt;h2 id=&quot;context&quot; tabindex=&quot;-1&quot;&gt;Context&lt;/h2&gt;
&lt;p&gt;I had a very interesting discussion with &lt;a href=&quot;https://twitter.com/remisan&quot;&gt;Rémi&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/maffpool&quot;&gt;Matthieu&lt;/a&gt;, two of my coworkers, today about caching. We encounter some performances issues on the &lt;a href=&quot;http://www.evaneos.com/&quot;&gt;Evaneos.com website&lt;/a&gt; and were arguing about the fact to place some cache between our API and our frontend.&lt;/p&gt;
&lt;p&gt;For the sake of the argument we took the example of our itinerary list. View it as a product list on some other online store.&lt;/p&gt;
&lt;p&gt;As we all agreed that setting a cache-control header stating that the current list of products will greatly improve performance issue because a copy could be kept in memory in the frontend website two points were made against having a cache.&lt;/p&gt;
&lt;p&gt;First one is that the user adding the product to the list might want to see it right away on the website. Funny thing is that this type of user is also interested in having a really fast website and as we are working in the same office we probably can come to an arrangement balancing time before display and performances.&lt;/p&gt;
&lt;p&gt;Second one is the one I want to tackle down here : &amp;quot;What if a user clicks on the link in the list and arrives on a 404 page because the product is no longer available?&amp;quot;&lt;/p&gt;
&lt;p&gt;Note : I’m not taking Etag system into account here because they are slower - you still need to make a call to the API - and as you’ll see after don’t help in anything anyway.&lt;/p&gt;
&lt;h2 id=&quot;you-already-have-this-problem&quot; tabindex=&quot;-1&quot;&gt;You already have this problem&lt;/h2&gt;
&lt;p&gt;So, what happened?&lt;/p&gt;
&lt;p&gt;A potential client sees a &lt;em&gt;cached&lt;/em&gt; list of products. One item has been removed from the list, but the cache has not been updated yet because of the cache policy. The client finds her dream product, unfortunately that is the one we just removed, and clicks on it.&lt;br&gt;
Let’s simplify here and say we are caching lists and not individual items, but this changes almost nothing. We now have the information that this product is not available - API returned 404/410 for instance - and our front website displays a 404 page to our client. This is bad UX.&lt;/p&gt;
&lt;p&gt;Let’s try without cache.&lt;/p&gt;
&lt;p&gt;A potential client sees a &lt;em&gt;non cached&lt;/em&gt; list of products. She browses the list looking for her dream product. While she’s looking at the list someone using our backend removes a product. Finally, she founds the product she wants - guess which one? - clicks on it and arrives on a 404 page. This is bad UX.&lt;/p&gt;
&lt;p&gt;We already have this issue.&lt;/p&gt;
&lt;p&gt;We can pretend that the time between product list presentation and the client choosing a product is so short that the chances that we are removing a product during this time are low and decide not to care, but we will probably have an issue later anyway (ex: when the user adds the product to the basket).&lt;/p&gt;
&lt;p&gt;Matthieu made an interesting observation when I bothered him later with all this, the website is not the only access to a product page : even when we don’t display a product in the list, search engines are still showing them anyway.&lt;/p&gt;
&lt;h2 id=&quot;what-do-we-do-now%3F&quot; tabindex=&quot;-1&quot;&gt;What do we do now?&lt;/h2&gt;
&lt;p&gt;We have a problem. Facing it is a good starting point.&lt;/p&gt;
&lt;p&gt;In my point of view I don’t see why this second point should take us away from a massive performance boost because the problem is still here anyway without cache.&lt;/p&gt;
&lt;h3 id=&quot;what-about-that-404-page%3F&quot; tabindex=&quot;-1&quot;&gt;What about that 404 page?&lt;/h3&gt;
&lt;p&gt;We probably can do something cleverer than just showing a 404 page. We know what the customer was looking for, why not display to her similar products for instance?&lt;/p&gt;
&lt;p&gt;We should go to business people and ask them what business opportunity we could take on this page instead of having a stupid 404 error.&lt;/p&gt;
&lt;p&gt;Saying all this I don’t see why any online store would like to have dumb 404 pages and this is why I wonder why they are still a thing?&lt;/p&gt;
&lt;p&gt;We can do better, improve UX, and as a business make more revenue.&lt;/p&gt;
&lt;p&gt;This makes me think a lot about this quote by &lt;a href=&quot;https://twitter.com/udidahan&quot;&gt;Udi Dahan&lt;/a&gt; :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;If you think you have a race condition, you don’t understand the domain well enough. These rules didn’t exist in the age of paper, there is no reason for them to exist in the age of computers. When you have race conditions, go back to the business and find out actual rules&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Race conditions are a hint that we probably don’t know enough and should ask more questions to business people, in order to come up with a better solution.&lt;/p&gt;
&lt;p&gt;Hey ! I’m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; too, if you want to chat about this or something else. Feel free to comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Refactoring VoteValidator with tests</title>
      <link href="https://blog.charlesdesneuf.com/articles/refactoring-votevalidator-with-tests/"/>
      <updated>2015-07-21T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/refactoring-votevalidator-with-tests/</id>
      <summary>Some lessons learned about testing</summary>
      <content type="html">&lt;p&gt;A few days ago I decided that the &lt;a href=&quot;/articles/tshirt-a-day-serie/&quot;&gt;t-shirt a day application&lt;/a&gt; doesn’t need the complexity of a vote validator with a validation handler and only a method stating if a vote is valid or not returning a boolean and then starting &lt;a href=&quot;https://github.com/SelrahcD/tshirtaday/compare/db07728ab8ca5142ce1ed73d7ffbeb3fcdc9f8ca...b9c1b02fea17059baa69f9c6b85c280684fe82d6&quot;&gt;refactoring&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The process of recfactoring was not painfull thanks to the test suite already there but I must admit that I have spoted some flaws in my VoteValidator tests.&lt;/p&gt;
&lt;h2 id=&quot;a-cleaner-votevalidator&quot; tabindex=&quot;-1&quot;&gt;A cleaner VoteValidator&lt;/h2&gt;
&lt;p&gt;A vote is valid if :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The voter has note already voted for a Tshirt for the given day&lt;/li&gt;
&lt;li&gt;The Tshirt has not been elected yet&lt;/li&gt;
&lt;li&gt;A voting session is opened for that day&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and I wanted the code to express this requirements clearly.&lt;/p&gt;
&lt;p&gt;The isValid has been refactored to use 3 private methods each one dealing with one rule :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;VoteValidator&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;isValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Vote&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$vote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;voterHasNotAlreadyVotedForDay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$vote&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;voterId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$vote&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tshirtExistsAndHasNotBeenElectedYet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$vote&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;tshirtId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aVotingSessionIsOpenedForDay&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$vote&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;day&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the point I started to think that I probably should be able to switch lines and still have the test going green and tried to do so. Well, tests were failing.&lt;/p&gt;
&lt;h2 id=&quot;testing-the-right-thing&quot; tabindex=&quot;-1&quot;&gt;Testing the right thing&lt;/h2&gt;
&lt;p&gt;I’ll take a simpler example that the one above with only two conditions to describe what I did wrong when I first wrote the tests.&lt;/p&gt;
&lt;p&gt;Let’s define a simple class :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Tuple&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A tuple is valid if :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Element A exists&lt;/li&gt;
&lt;li&gt;Element B exists&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The tuple validator would have been something like this :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;TupleValidator&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$elementARepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;ElementRepository&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$elementRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;elementRepository&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$elementRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;isValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Tuple&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementAExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementBExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;elementAExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;elementRepository&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;elementBExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;elementRepository&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the TupleValidatorTest the method asserting that isValid returns false if A doesn’t exist would have been :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;TupleValidatorTest&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$elementRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;elementRepository&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name class-name-fully-qualified static-context&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;ElementRepository&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;should_return_false_if_A_doesnt_exist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;B&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$validator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TupleValidator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$elementRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;elementRepository&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;exists&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;andReturn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertFalse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$validator&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The test will pass.&lt;/p&gt;
&lt;p&gt;Now if I invert the two test in the isValid method :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// TupleValidator&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;isValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Tuple&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementBExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementAExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;the test is failing because the ElementRepository mock wasn’t expecting a call for &amp;quot;exists&amp;quot; method with B as parameter.&lt;br&gt;
The implementation and the test are tightly coupled, which is bad.&lt;/p&gt;
&lt;p&gt;The mistake here is that I don’t set up the system properly. If I want to test that isValid returns false when A doesn’t exist I should ensure that B exists because I don’t want a false positive test passing because B is considered unexistant too.&lt;/p&gt;
&lt;p&gt;Let me introduce two more methods in the test class in order to make tests easier to read :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// TupleValidatorTest&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;elementExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;elementRepository&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;exists&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;andReturn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;elementDoesntExist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;elementRepository&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;exists&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;andReturn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now write the tests with the correct set up :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// TupleValidatorTest&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt; * @test&lt;br&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;should_return_false_if_A_doesnt_exist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;B&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$validator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TupleValidator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$elementRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementDoesntExist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertFalse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$validator&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This test will pass whatever the order of the assertions in isValid is.&lt;/p&gt;
&lt;p&gt;I think starting with the test for the positive result helps because you have to set up the whole system in order to make it pass and you have a template for all the negative results.&lt;/p&gt;
&lt;p&gt;Here are all the correct tests for the TupleValidator :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// TupleValidatorTest&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt; * @test&lt;br&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;should_return_true_if_A_and_B_exist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;B&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$validator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TupleValidator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$elementRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$validator&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt; * @test&lt;br&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;should_return_false_if_A_doesnt_exist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;B&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$validator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TupleValidator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$elementRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementDoesntExist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertFalse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$validator&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt; * @test&lt;br&gt; */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;should_return_false_if_B_doesnt_exist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;B&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$validator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TupleValidator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$elementRepository&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;elementDoesntExist&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertFalse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$validator&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isValid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$tuple&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When writing a test be sur to set up the system in a state that allows you to test what you think you are testing.&lt;/p&gt;
&lt;p&gt;This is a rather long post and even if I noticed something else during the refactoring I’ll stop now and keep this for a futur article.&lt;/p&gt;
&lt;p&gt;Hey ! I’m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; too, if you want to chat about testing or something else. Feel free to comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Test for behaviour not for implementation</title>
      <link href="https://blog.charlesdesneuf.com/articles/testing-for-behaviour-not-for-implementation/"/>
      <updated>2015-08-30T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/testing-for-behaviour-not-for-implementation/</id>
      <summary>Discover how to write maintainable tests by focusing on behavior instead of implementation details, reducing coupling and improving test reliability.</summary>
      <content type="html">&lt;p&gt;While working on &lt;a href=&quot;https://github.com/SelrahcD/SearchCache&quot;&gt;SearchCache&lt;/a&gt; I made a test which was ensuring that two shared search results coming from a same search - using the same search parameters - should be stored using the same key.&lt;/p&gt;
&lt;p&gt;Here it is :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testStoringSharedResultWithSameParametersUsesTheSameKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$searchCache&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SearchCache&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;searchResultStore&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;keyGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$params&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;name&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;test&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;age&#39;&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$result1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;AA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;HUG76767&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$result2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;AA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;AAA&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldStoreSharedResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$result1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$key1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$searchCache&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;storeSharedResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$result1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldStoreSharedResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$result2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$key2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$searchCache&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;storeSharedResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$result2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$key1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$key2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The method &lt;code&gt;shouldStoreSharedResult&lt;/code&gt; allows me to get the key used when the result is stored so I can later compare them for equality.&lt;/p&gt;
&lt;p&gt;This was not feeling right to me. It bothered me so much that I removed the test before adding it back. I had the uncomfortable impression that the test was knowing way to much thing about the system.&lt;/p&gt;
&lt;p&gt;As you can see the test is tightly coupled to the implementation and gives us some information about it. For instance we know that the system uses keys.&lt;/p&gt;
&lt;p&gt;Further thinking lead me to ask myself what I wanted to achieve with the system : I want the searchCache to replace an old version of a shared search cache with a newer one when possible.&lt;/p&gt;
&lt;p&gt;I made some &lt;a href=&quot;https://github.com/SelrahcD/SearchCache/commit/a7c20ce3a519592a8a814e8dd9d2d8eda70e738d&quot;&gt;refactoring on my tests&lt;/a&gt; in order to have tests reflecting what the system should do instead of how it does its job. Furthemore I decided that I could get rid of my mocks created with Mockery and use a &lt;a href=&quot;https://github.com/SelrahcD/SearchCache/blob/master/tests/Stubs/SearchResultsStores/InMemorySearchResultStore.php&quot;&gt;stub&lt;/a&gt; instead. The test now looks like this and doesn’t leak any details about the implementation :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testIfAPreviousVersionOfSharedResultIsStoredItsReplacedWhenANewOneIsStored&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$params&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;name&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;test&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;age&#39;&lt;/span&gt;  &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$result1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;AA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;HUG76767&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$result2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;AA&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Oho&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;storeContainsSharedResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$result1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;searchCache&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;storeSharedResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$result2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;searchCache&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getCopyOfSharedResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$result2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;searchCache&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$key&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I have the impression that using a mocking library pushed me on the coupling path and I’ll try to keep an eye on this in order to avoid making that same mistake again since I have already been proven guilty of this several times. Using mocks adds up a lot to the risk of creating false positive tests - &lt;a href=&quot;http://www.thoughtworks.com/insights/blog/mockists-are-dead-long-live-classicists&quot;&gt;see this article&lt;/a&gt; - and having a test suite that give you a false impression of confidence is probably worst than not having tests...&lt;/p&gt;
&lt;p&gt;Previous article was linked to a blog post where Fabio Pereira discussed the issue of writing a test which is a mirror of the implementation in his &lt;a href=&quot;http://fabiopereira.me/blog/2010/05/27/ttdd-tautological-test-driven-development-anti-pattern/&quot;&gt;Tautological Test Driven Development article&lt;/a&gt;. This is exactly what I was doing, Tautological TDD.&lt;/p&gt;
&lt;p&gt;I still have to rewrite some of the tests to decouple them from the implementation but I’m pretty confident that writing them with behaviour in mind and without a mocking library will help make them cleaner and simpler.&lt;/p&gt;
&lt;p&gt;I guess this episode taught me a valuable lesson. Cool.&lt;/p&gt;
&lt;p&gt;Hey ! I’m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; too, if you want to chat about testing or something else. Feel free to comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>OOP Layers</title>
      <link href="https://blog.charlesdesneuf.com/articles/oop-layers/"/>
      <updated>2015-09-16T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/oop-layers/</id>
      <summary>Please, stop breaking layers in OOP.</summary>
      <content type="html">&lt;p&gt;Yesterday at work I ran into a pretty nasty bug and while looking through the code I spotted something which even if it wasn&#39;t the primary cause of all the mess made it really worst.&lt;/p&gt;
&lt;p&gt;The code was going like this :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;AClassThatDoesSomethingWithThingsAndStuff&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;aMethodThatAssociatesSeveralThingsWithAStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$things&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$aStuff&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$things&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$aStuff&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aMethodThatUseASomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$aStuf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;aMethodThatAssociateOneThingWithAStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Thing&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Stuff&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;somehowGetAStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;associateWithASomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see it seems that we always need a Stuff in order to make an association with a Thing. Furthemore we hope to use that same Stuff for all the things.&lt;/p&gt;
&lt;p&gt;The main issue with this piece of code is the Stuff we use is going through two layers: the Stiff is created in a sub-layer (in &lt;code&gt;aMethodThatAssociateOneThingWithAStuff&lt;/code&gt;), going up one layer (in `aMethodThatAssociatesSeveralThingsWithAStuff) before returning to the sub-layer again.&lt;/p&gt;
&lt;p&gt;An other issue is that we are not really in control of the Stuff used.&lt;/p&gt;
&lt;p&gt;I think the code would have been better if it had been written this way :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;AClassThatDoesSomethingWithThingsAndStuff&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;aMethodThatAssociatesSeveralThingsWithAStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$things&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;somehowGetAStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$things&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aMethodThatUseASomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$aStuf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;aMethodThatAssociateOneThingWithAStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Thing&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Stuff&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;associateWithASomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code doesn&#39;t break the layers between the methods. The Stuff is get on the top level and passed one level down when needed. We are also able to choose what Stuff we want to pass to the sub-level method.&lt;/p&gt;
&lt;p&gt;And it&#39;s simpler...&lt;/p&gt;
&lt;p&gt;We can even make this way more fun if &lt;code&gt;aMethodThatAssociateOneThingWithAStuff&lt;/code&gt; can throw exceptions.&lt;br&gt;
The code will probably ended up with a try-catch structure inside of the foreach loop. (Our code end up like this at least...)&lt;/p&gt;
&lt;p&gt;See below :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;AClassThatDoesSomethingWithThingsAndStuff&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;aMethodThatAssociatesSeveralThingsWithAStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$things&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$aStuff&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$things&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;               &lt;span class=&quot;token variable&quot;&gt;$aStuff&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aMethodThatUseASomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$aStuf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token comment&quot;&gt;// Do what you want here to treat the exception&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;aMethodThatAssociateOneThingWithAStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Thing&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Stuff&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;somehowGetAStuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$thing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;associateWithASomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// Do something else, possibly throwing an exception&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$stuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Imagine the method &lt;code&gt;somehowGetAStuff&lt;/code&gt; has some side effects: writing a new Stuff in the database for instance.&lt;/p&gt;
&lt;p&gt;When an exception is thrown the execution enters the catch part of the try-catch and doesn&#39;t overwrite the value of &lt;code&gt;$aStuff&lt;/code&gt;.&lt;br&gt;
As long as the method will throw an exception without letting a chance to &lt;code&gt;$aStuff to be initialized &lt;/code&gt;somehowGetAStuff` will be called, with all its side effects...&lt;/p&gt;
&lt;p&gt;Breaking layers in OOP can have some really bad consequences and , as you can see, it makes the code harder to read and will probably create some bugs.&lt;/p&gt;
&lt;p&gt;This example is based on methods but classes are layers too and thus should not become entangled neither.&lt;/p&gt;
&lt;p&gt;As a rule of thumb you should probably be suspicious at a piece of code when you see:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A test for existence for something passed to your method.&lt;/li&gt;
&lt;li&gt;A memory allocation without desallocation.&lt;/li&gt;
&lt;li&gt;A layer in charge of retrieving something it needs. This is breaking the &lt;a href=&quot;https://en.wikipedia.org/wiki/Dependency_inversion_principle&quot;&gt;inversion of dependency principle&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A method called openSomething without a closeSomething, or the opposite, in the same layer.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;because this code is probably running through several layers.&lt;br&gt;
I guess we can make this list way longer.&lt;/p&gt;
&lt;p&gt;The header picture is a photography of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Quebrada_de_Humahuaca&quot;&gt;Quebrada de Humahuaca&lt;/a&gt; in Argentina. The strange look of the mountain, called &lt;a href=&quot;https://en.wikipedia.org/wiki/Cerro_de_los_Siete_Colores&quot;&gt;Cerro de los Siete Colores&lt;/a&gt; - The Hill of Seven Colors -, is the result of thousand years of marine sediment. See how beautiful layers can be ?&lt;/p&gt;
&lt;p&gt;I took the picture from this &lt;a href=&quot;http://www.toolito.com/montagnes-hornocal-colline-aux-7-couleurs-purmamarca/&quot;&gt;website&lt;/a&gt;, and I really don&#39;t know who I should thanks for it and attribute it to. I&#39;m not sure they do neither...&lt;/p&gt;
&lt;p&gt;Hey! I&#39;m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt;, if you want to discuss about OOP feel free to come by and say hi! You can comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Travis and different PHP versions in the same repo</title>
      <link href="https://blog.charlesdesneuf.com/articles/travis-and-different-php-versions-in-the-same-repo/"/>
      <updated>2015-11-22T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/travis-and-different-php-versions-in-the-same-repo/</id>
      <summary>How to set up Travis for different applications stored in the same repo but using different PHP versions.</summary>
      <content type="html">&lt;p&gt;At work we have one git repository for several projects. While this has some advantages their is also some drawbacks. Setting up a CI is one of them, especially when all the applications can&#39;t run on the same PHP version.&lt;/p&gt;
&lt;p&gt;We use Travis in order to ensure that our tests are still green after a modification but as we are currently working on some refactoring of our legacy app written for PHP 5.3 - We are late, we know... - we wanted to be able to work on a more recent PHP version. The issue is that Travis&#39; build matrix will fail either on the PHP 5.3 build or on the PHP 5.6 one.&lt;/p&gt;
&lt;p&gt;We finally came up with a solution, which even if not ideal allows us to see if we are breaking something : we run some of the tests only for one version or the other.&lt;/p&gt;
&lt;p&gt;First thing we create a variable for each version of PHP in the &lt;code&gt;before_script&lt;/code&gt; section of &lt;code&gt;travis.yml&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;before_install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; if &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;TRAVIS_PHP_VERSION&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; == &quot;5.3&quot; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;; then export PHP53=false; else export PHP53=true; fi&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; if &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;TRAVIS_PHP_VERSION&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; == &quot;5.6&quot; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;; then export PHP56=false; else export PHP56=true; fi&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice that we are assigning &lt;code&gt;false&lt;/code&gt; to the associated variable when the PHP version matches.&lt;/p&gt;
&lt;p&gt;Now, for each command that Travis should execute we are able to specify if it should be run for the current build version :&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $PHP53 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; ( cd $TRAVIS_BUILD_DIR/applications/app1 &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; composer install )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This must be read as &amp;quot;Cd to app1 directory and run composer install for PHP 5.3&amp;quot;.&lt;/p&gt;
&lt;p&gt;In the end our &lt;code&gt;travis.tml&lt;/code&gt; looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;language&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; php&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token key atrule&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5.3&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5.6&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token key atrule&quot;&gt;before_install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; if &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;TRAVIS_PHP_VERSION&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; == &quot;5.3&quot; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;; then export PHP53=false; else export PHP53=true; fi&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; if &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; $&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;TRAVIS_PHP_VERSION&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;0&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; == &quot;5.6&quot; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;; then export PHP56=false; else export PHP56=true; fi&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token key atrule&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $PHP53 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; ( cd $TRAVIS_BUILD_DIR/applications/app1 &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; composer install )&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $PHP56 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; ( cd $TRAVIS_BUILD_DIR/applications/app2 &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; composer install )&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token key atrule&quot;&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $PHP53 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; ( cd $TRAVIS_BUILD_DIR/applications/app1 &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./bin/phpunit )&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; $PHP56 &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; ( cd $TRAVIS_BUILD_DIR/applications/app2 &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./vendor/bin/phpspec run &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./vendor/bin/behat )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you want a command to be run for several PHP versions you can specify them as follow:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;install&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ( $PHP53 &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; $PHP56 ) &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; ( cd $TRAVIS_BUILD_DIR/applications/app1 &lt;span class=&quot;token important&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; composer install )&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;which should be read &amp;quot;Cd to app1 directory and run composer install for PHP 5.3 and PHP 5.6&amp;quot;.&lt;/p&gt;
&lt;p&gt;Handy, right ?&lt;/p&gt;
&lt;p&gt;Hey ! I&#39;m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; too. Come say Hi ! You can feel free to comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Removing ifs</title>
      <link href="https://blog.charlesdesneuf.com/articles/removing-ifs/"/>
      <updated>2016-05-08T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/removing-ifs/</id>
      <summary>What if we remove some if statements ?</summary>
      <content type="html">&lt;p&gt;I&#39;m not a big fan of if statements.&lt;/p&gt;
&lt;p&gt;They make source code harder to reason about, force us to write more test cases and increase &lt;a href=&quot;https://en.wikipedia.org/wiki/Cyclomatic_complexity&quot;&gt;cyclomatic complexity&lt;/a&gt;...&lt;/p&gt;
&lt;p&gt;We even debated with some colleagues on the way to lunch if it would be possible to make an application without any one of them. We finally agreed that it would be impossible.&lt;/p&gt;
&lt;p&gt;However in some cases it&#39;s totally doable to remove if statements. Here are a few options.&lt;/p&gt;
&lt;h2 id=&quot;keeping-them-outside-of-the-application&quot; tabindex=&quot;-1&quot;&gt;Keeping them outside of the application&lt;/h2&gt;
&lt;p&gt;The application I work on has to be integrated with some external services. Some of them ping our system on an a callback URL with an HTTP call. This is a fairly common scenario.&lt;/p&gt;
&lt;p&gt;I had to work on the controller action dealing with those callback calls. &lt;strong&gt;The controller action&lt;/strong&gt;. Yep. We have only one action for several externals services. Therefore the major part of the code is dealing with trying to recognize who is calling, base on the shape of the message. Coming with a lot of ifs.&lt;/p&gt;
&lt;p&gt;A simpler solution would have been to create one action for each external service, pushing the if statements outside of our code directly into the external services&#39; configuration.&lt;/p&gt;
&lt;h2 id=&quot;depency-inversion-principle&quot; tabindex=&quot;-1&quot;&gt;Depency inversion principle&lt;/h2&gt;
&lt;p&gt;Sometimes we have to make use of slightly different version of an algorithm, whether its for test purpose or for some other reason.&lt;/p&gt;
&lt;p&gt;I&#39;ve stumble upon a piece of code that was using a boolean flag specifying if code was run in test mode or not. A trivial example could be something like this :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Writter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$test&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Test : &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;TypeMachine&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$writter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$isInTestMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Writter&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$writter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$isInTestMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;writter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$writter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;isInTestMode&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$isInTestMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;isInTestMode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code has some flaws.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;TypeMachine&lt;/code&gt; has to implement some sort of logic in order to choose the value of the second parameter and this is not it&#39;s responsabilty to deal with that. This creates coupling between &lt;code&gt;TypeMachine&lt;/code&gt; and &lt;code&gt;Writter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;A better solution would be to inject a different &lt;code&gt;Writter&lt;/code&gt; when running in test mode :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Writter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;SimpleWritter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;TestWritter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;Test : &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;TypeMachine&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$writter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Writter&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$writter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;writter&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$writter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;writter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have added a &lt;code&gt;Writter&lt;/code&gt;interface and two implementations, &lt;code&gt;SimpleWritter&lt;/code&gt;and &lt;code&gt;TestWritter&lt;/code&gt;. We can now choose which one we want to use regarding of the context.&lt;br&gt;
As you can see the code of the &lt;code&gt;TypeMachine&lt;/code&gt; class is simpler too and all classes are easily testable.&lt;/p&gt;
&lt;p&gt;As an aside we should be carefull when we start using flag parameters in method prototype because it usually indicates some sort of missing concept. &lt;a href=&quot;https://blog.8thlight.com/dariusz-pasciak/2015/05/28/alternatives-to-boolean-parameters.html&quot;&gt;A cool article&lt;/a&gt; was written on 8th Light blog on how to remove this sort of parameters.&lt;/p&gt;
&lt;h2 id=&quot;null-object-pattern&quot; tabindex=&quot;-1&quot;&gt;Null object pattern&lt;/h2&gt;
&lt;p&gt;An other common use of if conditionals is to determine if an optional colaborator was provided before performing an operation on it.&lt;/p&gt;
&lt;p&gt;One of the best example is the use of a logger :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$logMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;TypeMachine&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;setLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;was typed.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;		&lt;span class=&quot;token comment&quot;&gt;// do something&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every time we want to log something we have to check whether the logger was set or not.&lt;/p&gt;
&lt;p&gt;Using the Null object pattern, which consists in creating an implementation of an interface with no behaviour, we can remove this checks.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$logMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;NullLogger&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$logMessage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// do nothing&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;TypeMachine&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NullLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;setLogger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;logger&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;logger&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$text&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;was typed.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;		&lt;span class=&quot;token comment&quot;&gt;// do something&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the &lt;code&gt;TypeMachine&lt;/code&gt; is instanciated its logger property is set to an instance of &lt;code&gt;NullLogger&lt;/code&gt; allowing us to remove each &lt;code&gt;if($this-&amp;gt;logger)&lt;/code&gt; checks.&lt;/p&gt;
&lt;p&gt;If you want to learn more about the Null object pattern you definitely should look at &lt;a href=&quot;https://www.youtube.com/watch?v=29MAL8pJImQ&quot;&gt;Sandy Metz&#39;s Nothing is something talk&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I hope this article will help removing some if statements in this world making it a better place. If you have some other ways to remove if statements please share them !&lt;/p&gt;
&lt;p&gt;Hey! I&#39;m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt;, if you want to discuss about OOP feel free to come by and say hi! You can comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Write code for the dumbest person you know</title>
      <link href="https://blog.charlesdesneuf.com/articles/write-code-for-the-dumbest-person-you-know/"/>
      <updated>2016-05-27T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/write-code-for-the-dumbest-person-you-know/</id>
      <summary>I&#39;m dumb and I can&#39;t understand your clever code.</summary>
      <content type="html">&lt;p&gt;&lt;em&gt;I wrote this text a few months ago while I was angry and decided not to publish it right away, letting it sink in. I kept it, made a few changes and finally decided to publish it.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;i&#39;m-dumb&quot; tabindex=&quot;-1&quot;&gt;I&#39;m dumb&lt;/h2&gt;
&lt;p&gt;You&#39;re lucky. You&#39;re lucky because you&#39;re clever and I&#39;m not.&lt;/p&gt;
&lt;p&gt;I noted that I was dumb when I was unable to understand how some data was injected into an object during its instantiation.&lt;br&gt;
I know what a depency injection manager is but that time was not like the others. It took me one hour to figure where that configuration parameter was coming from. I had to go through 8 code files and 3 configuration files and I had to make several full text searches in order to jump from file to file because following the code was misleading. This exercise was really hard to me because I&#39;m barely able to remember my own phone number so I had to write down to paper your way to do DI because I couldn&#39;t keep it in memory.&lt;/p&gt;
&lt;p&gt;I have to say that I started to be pissed off - allright, this is not uncommon - because your code was telling me that I was stupid, and no one likes beeing told he is stupid.&lt;/p&gt;
&lt;p&gt;Someone then came by and told me it was the tool, a cool feature provided by the tool, that you were using. Again, I know what dependency injection manager is, and how it works, but that one magical feature which allows you to make the same thing as usual but in a very clever way, I don&#39;t know it. Probably because I had never needed it. I guess appart from beeing dumb I also lack of culture about that tool features.&lt;/p&gt;
&lt;p&gt;I was sure that I was dumb when I couldn&#39;t understand what you&#39;re abstraction was for. For me all the interesting thing your code was doing was concentrated in one class. That one class I found after going through several classes doing I don&#39;t really know what. That one class which was wrapping a class from a library.&lt;/p&gt;
&lt;p&gt;I probably missed something but I have to say that &lt;a href=&quot;https://twitter.com/Selrahcd/status/701794483726323712&quot;&gt;I started to be pissed off&lt;/a&gt; again - I think there is some kind of pattern here. I tried to understand, because, after all, you&#39;re clever. Why would you do something that complicated when just using the class from the library, or your wrapper, would have done the job ? Maybe you were protecting us from the coupling to that library and making its use simpler. I have to confess that I was surprised when I saw a massive chain of getters going all the way down to get the library object you were wrapping and pass it to the some other object.&lt;/p&gt;
&lt;p&gt;Those are some of the times I noticed I was dumb. Hard truth...&lt;/p&gt;
&lt;p&gt;Thank you for beeing an eye opener.&lt;/p&gt;
&lt;h2 id=&quot;i-used-to-be-clever-though&quot; tabindex=&quot;-1&quot;&gt;I used to be clever though&lt;/h2&gt;
&lt;p&gt;I used to be clever though. I used to write some pretty pieces of code doing all kind of cool stuff. I was theorically able to change the way the whole system was working by changing one parameter in a configuraion file. My code was able to cope with all future issues I had foreseen. I knew how to do some tricky things with all sort of tools.&lt;/p&gt;
&lt;p&gt;But time goes on and aging probably made me dumb.&lt;/p&gt;
&lt;p&gt;Aging or discoveries and failures.&lt;/p&gt;
&lt;p&gt;Abstractions are good when you know what you&#39;re abstracting. Apparently you only know what your abstracting based on the information you currently have. I couldn&#39;t predict future and the next needs. I tried though, I failed, and ended up &lt;a href=&quot;http://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction&quot;&gt;locked down by my clever abstraction&lt;/a&gt;. And as we all do I started to cheat, broke my abstraction, used getters in order to get the internals and be able to manipulate them.&lt;/p&gt;
&lt;p&gt;Not once but several times.&lt;/p&gt;
&lt;p&gt;I made complicated stuff too. They looked hard to understand at first glance but once you managed to wrap your head around the problem being solved, everyone could see how legant the solution was. At least, I was. I wrote it after all. Then I moved on, worked on something else and had to came by to it later probably because of something to fix. It seems like complicated code tend to be buggy. Worst, apparently time had increased code complexity. I was now unable to understand easily what was going on with this code and all the choices I made a few days before.&lt;/p&gt;
&lt;p&gt;I have seen people reluctant to use some of my code. In my opinion it was not that hard to understand but they probably needed someone to guide them the first time they used the library because it was magic in some kind of way. We had a solution, we probably should have written documentation. Or simpler code : &lt;a href=&quot;http://www.infoq.com/presentations/8-lines-code-refactoring&quot;&gt;something the junior of your team can grasp by himself&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I was pissed with myself on those occasions.&lt;/p&gt;
&lt;p&gt;I guess some time you will be pissed with yourself too. I hope so. It means your code is used, and that&#39;s cool.&lt;/p&gt;
&lt;h2 id=&quot;please-stop&quot; tabindex=&quot;-1&quot;&gt;Please stop&lt;/h2&gt;
&lt;p&gt;Please stop writing complicated stuff. It&#39;s harder to read, to understand, to test, and even to produce. Moreover it&#39;s unnecessary, it doesn&#39;t protect you from future requirements, it&#39;s hard and it costs time and money.&lt;/p&gt;
&lt;p&gt;Please stop doing something everyone knows about in some other clever way. It&#39;s cool that you know everything about your tools, but what will be cooler is that you know when to use or not a feature. &lt;a href=&quot;https://en.wikipedia.org/wiki/Principle_of_least_astonishment&quot;&gt;I don&#39;t want to be suprised while reading code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I want to read code as I read books. Simple ones. Like Harry Potter. Translated in french. As I said, I&#39;m dumb and I&#39;m lazy...&lt;/p&gt;
&lt;p&gt;Take a break, think two minutes about the piece of code you&#39;re about to craft : can you make it simple ? Can the dumbest person you know understand it ? Will he be surprised ?&lt;/p&gt;
&lt;p&gt;As you&#39;ve been an eye opener to me I hope these words will be an eye opener to you too.&lt;/p&gt;
&lt;p&gt;Go write some code. Simple one.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Thanks a lot to &lt;a href=&quot;https://twitter.com/remisan&quot;&gt;Remi&lt;/a&gt; and &lt;a href=&quot;https://twitter.com/ouarzy&quot;&gt;Emilien&lt;/a&gt; for reviewing this post.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Whether you&#39;re clever or not we can discuss on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt;. Feel free to comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Stop using the database for everything</title>
      <link href="https://blog.charlesdesneuf.com/articles/stop-using-database/"/>
      <updated>2016-06-07T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/stop-using-database/</id>
      <summary>I think we should start using database less.</summary>
      <content type="html">&lt;p&gt;First thing first, database are useful in a lot of cases and we need them. We need them when we have to store information provided by users or information changing on a regular basis.&lt;/p&gt;
&lt;p&gt;This is not the case of all data an application needs in order to run - currencies, countries, languages, application settings, to name a few. This types of information don&#39;t change that often and adding a database for them doesn&#39;t provide much value. Maybe a developer won&#39;t be needed in order to change or add a value but I&#39;m inclined to think that more than often no tool is provided to business people to change it by themselves, and a developer will have to run a query on the database nevertheless.&lt;/p&gt;
&lt;p&gt;Adding a database in those cases is a cost with no value added. More code is needed, additional complexity is created, a database has to be maintained and the resulting system is probably slower - not in an important order of magnitude of course - than the same system without a database.&lt;/p&gt;
&lt;p&gt;These additional costs are not the main reasons I dislike the use of database for such information.&lt;/p&gt;
&lt;p&gt;In the long run special cases will probably be introduced in the application - do this for this country and that for that other one for instance. In order to face these special cases basically two solutions exist. Either you add the differentiating piece of data in the database, which might mean changing table schema and so on or you start cheating and use the reference - id, ISO standard reference - of the concept with a special case thus coupling code and database content altogether.&lt;/p&gt;
&lt;p&gt;From what I&#39;ve seen we, developers, will choose solution 2, maybe because we are lazy, in the middle of a rush or don&#39;t mesure the consequences of it.&lt;/p&gt;
&lt;p&gt;Once the coupling is created it will eventually grow to the point where almost all data from the database is stored in the code or in a configuration file dedicated to mappings between code artifacts and database stored values. In the end a developper intervention will be necessary every time to update theses pieces of code. Back to square one.&lt;/p&gt;
&lt;p&gt;If the data doesn&#39;t change much why not store it in the code after all ? Having only one golden source that is easily changeable - adding a property to a value object is way easier than changing a table schema - instead of two or more sources that will have to be maintained in cohesion.&lt;/p&gt;
&lt;p&gt;When you&#39;re dealing with some data that doesn&#39;t look likely to change often try not to use a database. Start with a simple representation based on value objects if you are into POO. If in the long run that information appears to change a lot then use a database. Start with the simpler solution that works.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Once again, thanks a lot to &lt;a href=&quot;http://twitter.com/remisan&quot;&gt;Remi&lt;/a&gt; for his feedbacks.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you are a DBA, hate me, and want to tell me that I&#39;m an awful person I&#39;m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt;. Feel free to comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Unique instance anti-pattern</title>
      <link href="https://blog.charlesdesneuf.com/articles/unique-instance-anti-pattern/"/>
      <updated>2016-06-10T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/unique-instance-anti-pattern/</id>
      <summary>A finalist for the worst anti-pattern tournament.</summary>
      <content type="html">&lt;p&gt;I&#39;m back from the past. I have been trying to introduce some tests in one of the oldest part of our codebase. I&#39;m back from a time where using &lt;a href=&quot;http://martinfowler.com/articles/injection.html#UsingAServiceLocator&quot;&gt;Service Locator&lt;/a&gt; was the norm for a lot of people, &lt;a href=&quot;http://martinfowler.com/articles/injection.html#InversionOfControl&quot;&gt;Inversion of Control&lt;/a&gt; concept wasn&#39;t widespread and &lt;a href=&quot;https://en.wikipedia.org/wiki/Singleton_pattern&quot;&gt;Singleton&lt;/a&gt; wasn&#39;t presented as an anti-pattern yet.&lt;/p&gt;
&lt;p&gt;I have to say that I feel funny pointing to some articles from Uncle Bob from 2004 while I&#39;m speaking about a code that is less that 7 years old.&lt;/p&gt;
&lt;p&gt;As one of my co-worker said last week :&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It&#39;s funny to see that some people were 10 years ago where we are now. I&#39;m wondering what they are doing now and where we will be in 10 years.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;(F*** this quote is gigantic. Ok, I&#39;m mixing message and form here...)&lt;/p&gt;
&lt;p&gt;I do too.&lt;/p&gt;
&lt;p&gt;So, here goes the masterpiece.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;AClassWithADecentName&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$alreadyInstantiated&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword static-context&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$alreadyInstantiated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;You can&#39;t instantiate that class twice&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token keyword static-context&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$alreadyInstantiated&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token delimiter important&quot;&gt;?&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you try to instantiate it twice your whole application will blow up because of the exception.&lt;/p&gt;
&lt;p&gt;This is worst than a singleton : you can&#39;t instanciate an object twice and you can&#39;t use the first created instance in an other scope than the one where it was created, unlike the singleton. This is the &lt;strong&gt;unique instance anti-pattern&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It turns out I got the chance to speak with the man who created that beast. Remember my co-worker from last week ?&lt;/p&gt;
&lt;p&gt;We spoke about the context at the time he wrote that piece of code. The team was composed of unexperienced/stubborn developers and he was trying to enforce a design and he wanted to prevent the misuse of that class. To be fair a more appropriate name for the class could be &lt;code&gt;AClassWithADecentNameDoingThingWeAreNotVeryProudOfInAWayWeDislike&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The thing is that class is untestable the way it is now and the unique reason is to prevent developers to do something. I strongly believe that explanations and code reviews are way more powerful to prevent bad code from being created in the long term and so I don&#39;t want to harm code design as a protection against other developers. They have access to the codebase and they can mess with it anyway.&lt;/p&gt;
&lt;p&gt;Protection code passes a message - do not use me - that should have been transmitted in some other form - teaching -.&lt;/p&gt;
&lt;p&gt;You know : Give a man a fish... Teach a man to fish...&lt;/p&gt;
&lt;p&gt;In order to prevent some other beasts from being created let&#39;s make a deal : We will not damage our code to prevent other developers to do something and we will explain why something should be done or not instead.&lt;/p&gt;
&lt;p&gt;We will not conflate the message and the form anymore.&lt;/p&gt;
&lt;p&gt;If you love bad images, bad jokes and anti-pattern I&#39;m on&lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt;. Feel free to comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Extra readable tests</title>
      <link href="https://blog.charlesdesneuf.com/articles/extra-readable-tests/"/>
      <updated>2016-08-10T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/extra-readable-tests/</id>
      <summary>Learn proven techniques to write highly readable tests using snake-case naming, builders, helper methods, and the Arrange-Act-Assert pattern. Transform complex test code into clear, maintainable documentation.</summary>
      <content type="html">&lt;p&gt;I’ve been doing some experiments with tests for the last few weeks. In this article I’ll share some tricks I’ve learned from others, mainly &lt;a href=&quot;https://www.youtube.com/watch?v=XHnuMjah6ps&quot;&gt;Sandro Mancuso&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/watch?v=1_dpOZmKXBw&quot;&gt;Matthias Verraes&lt;/a&gt;, and, of course, from &lt;a href=&quot;http://www.growing-object-oriented-software.com/&quot;&gt;Growing Oriented Object Software Guided By Tests&lt;/a&gt; - the GOOS - by Nat Pryce and Steve Freeman.&lt;/p&gt;
&lt;p&gt;In this article I will refactor the following test one step at a time in order to make it even more readable than it is right now.&lt;br&gt;
The test framework I’m using here is &lt;a href=&quot;https://phpunit.de/&quot;&gt;PhpUnit&lt;/a&gt; but the following technics work with other frameworks - I’ve applied some of them with &lt;a href=&quot;http://www.phpspec.net/&quot;&gt;PhpSpec&lt;/a&gt;. By the way some technics are heavily inspired by this great tool.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;SendWelcomeEmailToMemberTest&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPUnit_Framework_TestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;tearDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WelcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;snake-case-test-name&quot; tabindex=&quot;-1&quot;&gt;Snake-case test name&lt;/h2&gt;
&lt;p&gt;As you can see I don’t use the classical PhpUnit test naming form, &lt;code&gt;testWhatItSupposedToDo&lt;/code&gt;, but prefer to use snake-case notation and to start test name with &lt;code&gt;it_&lt;/code&gt; - which forces me to use the &lt;code&gt;@test&lt;/code&gt; annotation. Starting the test name with &lt;code&gt;it_&lt;/code&gt; helps me to think harder in order to come up with a good name for the test.&lt;/p&gt;
&lt;p&gt;Having good test name open an interesting possibility: being able to generate a documentation readable by all members of the project using &lt;a href=&quot;https://phpunit.readthedocs.io/fr/latest/textui.html#testdox&quot;&gt;PhpUnit’s testdox option&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;arrange---act---assert&quot; tabindex=&quot;-1&quot;&gt;Arrange - Act - Assert&lt;/h2&gt;
&lt;p&gt;First change is to reorganize the test to be in the form of &lt;code&gt;Arrange - Act - Assert&lt;/code&gt;. Having all tests following this model helps knowing what the expectations are, as they all are at the end of the scenario. In the current version an assertion - an email should be sent - is made before the acting step.&lt;/p&gt;
&lt;p&gt;The change here is to use a &lt;em&gt;Spy&lt;/em&gt; instead of a &lt;em&gt;Mock&lt;/em&gt;. You can read on the different types of doubles &lt;a href=&quot;https://martinfowler.com/bliki/TestDouble.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// Arrange    &lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Use a spy instead of a mock&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WelcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// Act&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// Assert&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldHaveReceived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;builder&quot; tabindex=&quot;-1&quot;&gt;Builder&lt;/h2&gt;
&lt;p&gt;Let’s now imagine that the class &lt;code&gt;Member&lt;/code&gt; is central to our application and is used in a lot of tests, all of them having to call the constructor in order to get &lt;code&gt;Member&lt;/code&gt;s.&lt;/p&gt;
&lt;p&gt;A new requirement is made : A member should have a birth date. We must add an extra parameter to all calls to &lt;code&gt;Member&lt;/code&gt; constructor to fix all the failing tests. Trust me, the day you’ll face this situation you probably be very disappointed.&lt;/p&gt;
&lt;p&gt;A solution is to introduce a &lt;a href=&quot;https://en.wikipedia.org/wiki/Builder_pattern&quot;&gt;builder&lt;/a&gt; which will encapsulate the call to &lt;code&gt;Member&lt;/code&gt; constructor. We are now able to do the change in only one place.&lt;/p&gt;
&lt;p&gt;Using a builder for member and email gives us the following test :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WelcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withFirstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withEmailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeEmail&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withSubject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldHaveReceived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$welcomeEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code for &lt;code&gt;MemberBuilder&lt;/code&gt; is the following :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;MemberBuilder&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$firstname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$emailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$birthDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * MemberBuilder constructor.&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$faker&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name class-name-fully-qualified static-context&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Faker&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Factory&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;emailAddress&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$faker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;firstname&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$faker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;firstname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;birthDate&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$faker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;firstname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;emailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;birthDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @param $firstname&lt;br&gt;     * @return MemberBuilder&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;withFirstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$firstname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;firstname&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$firstname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @param $emailAddress&lt;br&gt;     * @return MemberBuilder&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;withEmailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$emailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;emailAddress&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$emailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @param $birthDate&lt;br&gt;     * @return MemberBuilder&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;bornOn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$birthDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;birthDate&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$birthDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the builder constructor provides a default value for each property of &lt;code&gt;Member&lt;/code&gt;. This is really convenient because it allows to provide only the properties that matters for the test we are writing. If I need a &lt;code&gt;Member&lt;/code&gt; and only his firstname matters I can call &lt;code&gt;(new MemberBuilder)-&amp;gt;withFirstName(&#39;John&#39;)-&amp;gt;build()&lt;/code&gt;. This way we can reduce the noise in the test, displaying only the relevant bits of information, making the test easier to understand.&lt;/p&gt;
&lt;p&gt;Moreover I’m using &lt;a href=&quot;https://github.com/fzaninotto/Faker&quot;&gt;Faker&lt;/a&gt;, a library helping to create fake data for a lot of types. Using random data ensures tests are not making any assumptions based on a fixed value somewhere.&lt;/p&gt;
&lt;h2 id=&quot;builder-functions&quot; tabindex=&quot;-1&quot;&gt;Builder functions&lt;/h2&gt;
&lt;p&gt;If we want to make the test even easier to read we can hide the call to the builders behind a function. If we introduce the two following functions :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;aMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;anEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can rewrite our test like this :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WelcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;aMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Creates a member&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withFirstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withEmailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeEmail&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;anEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Creates an email&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withSubject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldHaveReceived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$welcomeEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;helper-methods&quot; tabindex=&quot;-1&quot;&gt;Helper methods&lt;/h2&gt;
&lt;p&gt;If we want to write our tests using the business vocabulary we can introduce helper methods as following :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;SendWelcomeEmailToMemberTest&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPUnit_Framework_TestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;mailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;welcomeMailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WelcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;tearDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;it_exists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withFirstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withEmailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;it_should_send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;anEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withSubject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_exists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;MemberBuilder&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_should_send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;EmailBuilder&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;mailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldHaveReceived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see the test reads easily and business people would be able to understand what is going on reading it. The test can now serve as documentation both for developers and business people.&lt;/p&gt;
&lt;h2 id=&quot;going-further&quot; tabindex=&quot;-1&quot;&gt;Going further&lt;/h2&gt;
&lt;h3 id=&quot;create-plug-in-for-the-test-framework&quot; tabindex=&quot;-1&quot;&gt;Create plug-in for the test framework&lt;/h3&gt;
&lt;p&gt;The test could be even clearer if we could replace &lt;code&gt;$this&lt;/code&gt; before in the call to helper methods by &lt;code&gt;given()&lt;/code&gt; and &lt;code&gt;then()&lt;/code&gt;. I made the following code work using a class with a static property but I think we could do something better with a plug-in for the test framework.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;it_exists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withFirstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withEmailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;it_should_send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;anEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withSubject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;generate-documentation&quot; tabindex=&quot;-1&quot;&gt;Generate documentation&lt;/h3&gt;
&lt;p&gt;I think the tests are clear enough but if the business people are really reluctant to look at code or to deal with versioning systems we probably can parse the test class and generate a documentation in a better format for them.&lt;/p&gt;
&lt;p&gt;All these technics can be used one at a time as they solve different problems but when combined they give a really good result.&lt;/p&gt;
&lt;p&gt;As always using them as a cost which should be balanced regarding the interest of the project you are working on.&lt;/p&gt;
&lt;p&gt;I hope these tricks will help you write tests you’re happy with in the long run.&lt;/p&gt;
&lt;p&gt;And if you need some help with your test I think I can help. Have a look at &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;my video course&lt;/a&gt; in French or &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let&#39;s have a chat&lt;/a&gt; and see what we can do together.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>It should not throw an exception</title>
      <link href="https://blog.charlesdesneuf.com/articles/it-should-not-throw-an-exception/"/>
      <updated>2016-08-27T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/it-should-not-throw-an-exception/</id>
      <summary>Discover why &#39;should not throw exception&#39; is a poor test method name and learn proven strategies for writing descriptive, maintainable test names.</summary>
      <content type="html">&lt;p&gt;I’m very interested in living documentation because it provides some great values. First, you finally have documentation, which is up to date and under version control. The other great thing about it is that it forces to look at the code through another perspective : through the eyes of a non developer person.&lt;/p&gt;
&lt;p&gt;A few weeks ago I experimented and extracted documentation from code and tests. Tests were used to describe an object properties, its business rules. Some test method names were feeling a bit to technical to me and were not making sense to business people.&lt;/p&gt;
&lt;p&gt;In the case of a DatePeriod object two tests names were odd :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;it is initializable&lt;/li&gt;
&lt;li&gt;it throws an exception if end date precedes start date&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first one is created automatically by PhpSpec in order to ensure the DatePeriod class is created. This test case should be removed as soon as another test case is created as it doesn’t provide value anymore.&lt;/p&gt;
&lt;p&gt;The second one is more interesting. It doesn’t describe what the DatePeriod does but how it does it. This is a form of coupling between the test case and the implementation. &lt;a href=&quot;/articles/testing-for-behaviour-not-for-implementation&quot;&gt;As seen before&lt;/a&gt; coupling between the two of them is not a good idea because it makes harder for coming up with another implementation. In that case throwing an exception is probably the best implementation though.&lt;/p&gt;
&lt;p&gt;What could be a better name then? We’re looking for something that speaks to business people. &amp;quot;It doesn’t allow end date to precede start date&amp;quot; does the job here.&lt;/p&gt;
&lt;p&gt;I think looking at the code through the prism of documentation really helps making it better. I’m always amazed to see how business understanding, modeling, testing and documentation really fit together - 4 sides of the same coin (Sorry).&lt;/p&gt;
&lt;p&gt;Hey! I’m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; I only throw some bad jokes but no exception. Feel free to comment below as well&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Ask about the freelancer</title>
      <link href="https://blog.charlesdesneuf.com/articles/ask-about-the-freelance/"/>
      <updated>2016-11-26T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/ask-about-the-freelance/</id>
      <summary>You don&#39;t know the full story until the end of it.</summary>
      <content type="html">&lt;p&gt;A few days ago I was involved in a &lt;a href=&quot;https://twitter.com/webdevilopers/status/798566284615094272&quot;&gt;discussion on twitter about a modeling problem&lt;/a&gt;. As a support for the discussion we were talking about a time tracking system and the concept of time sheet. I proposed two ways I would model the requirements. I indicated that I would choose one solution over the other if the concept of time sheet was really relevant for tracking, and that I couldn&#39;t find an example where it would be the case.&lt;/p&gt;
&lt;p&gt;A few hours later I thought that a freelancer would need to have more than one time sheet opened at the same time if she worked for several clients.&lt;/p&gt;
&lt;p&gt;At work we also have a concept of freelancers that wasn&#39;t really taken into account during the creation of the model which forces us to do some workarounds in order to deal with them.&lt;/p&gt;
&lt;p&gt;This two things in mind it stroked me that freelancers make for great personas when you&#39;re speaking with business people and trying to find out the properties of a system.&lt;/p&gt;
&lt;p&gt;Freelancers have two really interesting properties:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They can work for several companies/groups at the same time.&lt;/li&gt;
&lt;li&gt;It&#39;s easy to think they&#39;ll leave.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This two properties open for really insightful discussions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What should happen when a user leaves the system?&lt;/li&gt;
&lt;li&gt;Is something owned by the user or the group she&#39;s belonging to?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have the impression that the leaving property all by itself helps discovering corner cases in the domain and prevents making modeling mistakes.&lt;/p&gt;
&lt;p&gt;It&#39;s never just someone who stops using the system.&lt;/p&gt;
&lt;p&gt;For instance we probably have to transfer something linked to a leaving person to someone else in the same group. Does it means that this thing is owned by the group and is taken care of by a member of the group at a given moment? Therefore should we model explicitly the relation between that thing and the group (the thing is owned by the group and taken care of by that person) or is the relation between the thing and the person enough (the thing is owned by the group because the thing is taken care of by the person who belongs to the group)?&lt;/p&gt;
&lt;p&gt;Asking about what happen in the end can help understand what are the true relationships between domain concepts.&lt;/p&gt;
&lt;p&gt;I guess that this observation can be generalized to all model objects, not only to person.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Asking for what happen to something reaching an end-of-life state leads to better insights as you don&#39;t know the full history until you know the end of it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hey! I&#39;m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; and if you don&#39;t want to consider this as a finished thing I&#39;ll be happy to talk with you about it there! You can comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>What if someday...</title>
      <link href="https://blog.charlesdesneuf.com/articles/what-if-someday/"/>
      <updated>2017-04-15T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/what-if-someday/</id>
      <summary>Don&#39;t pretend you know what will come next</summary>
      <content type="html">&lt;p&gt;&amp;quot;What if someday...&amp;quot;&lt;/p&gt;
&lt;p&gt;These three words are probably one of the main causes of accidental complexity and late delivery in the software industry. As software developers we always try to guess what the future will be, to anticipate future needs, in order to be prepared. The issue is that it&#39;s something we suck at.&lt;/p&gt;
&lt;p&gt;We try to predict what will be the next features needed and doing so we add a lot of complexity to the software we build and maintain.&lt;/p&gt;
&lt;p&gt;Nothing comes for free. Adding that new frontend framework, that queuing system or that cool design pattern you just learned about to the project has a cost and the team will have to support it in the long run. Building it this way will probably be longer than a simpler solution. You&#39;ll need to have someone around able to understand it and to maintain it. You might have to adapt your tooling for deployment, testing and logging. You may see your build time increase. You can expect to deal with some new failures you weren&#39;t aware of. This are a few examples but I&#39;m sure we can find some others.&lt;/p&gt;
&lt;p&gt;So, here a few other questions, as valid as the previous one:&lt;/p&gt;
&lt;p&gt;What if the day we have to scale for a large number of users never comes?&lt;/p&gt;
&lt;p&gt;What if by the day we actually have to offer offline service we can use some other tool better suited for that need?&lt;/p&gt;
&lt;p&gt;What if we&#39;ve imagined all the possible future requirements and made a system that can deal with them all except one  That one we have to implement after all, that one we can&#39;t introduce because we&#39;ve been too clever and have created a system locked by its abstractions.&lt;/p&gt;
&lt;p&gt;Sounds familiar?&lt;/p&gt;
&lt;p&gt;Deciding to implement something based on assumption is basically taking a bet, which means there is probabilty to lose something, probably time and/or money. Our job as software developper is to reduce the risk of building something wrong or useless and to minimize the loss in case we built it wrong after all.&lt;/p&gt;
&lt;p&gt;Using a simple solution, or even a boring one, will allow to deploy the feature earlier than trying to build a full system able to cope with everything.&lt;br&gt;
This means we&#39;ll deliver value earlier, possibly making money earlier. This also means we&#39;ll have user feedback earlier and we&#39;ll be able to take advantage of the feedback loop to test our assumptions and see if we&#39;re building the right thing. This is where the heart of agile is.&lt;br&gt;
We&#39;ve also saved some time we can use to build something else on another part of the system or building a prototype of the more complexe solution and learn about it.&lt;/p&gt;
&lt;p&gt;Striving for simplicity is a good way to maximise the value of our system.&lt;/p&gt;
&lt;p&gt;Next time you have to choose whether you should introduce a new tool to your stack please take the time to think and reflect on all the tradeoffs coming along.&lt;/p&gt;
&lt;p&gt;Hey! I&#39;m ranting on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; too and you can come and tell me I&#39;m wrong over there if you want to! You can comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Naming collision</title>
      <link href="https://blog.charlesdesneuf.com/articles/naming-collision/"/>
      <updated>2017-08-28T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/naming-collision/</id>
      <summary>Use the same word everywhere and enjoy the mess.</summary>
      <content type="html">&lt;h2 id=&quot;the-context&quot; tabindex=&quot;-1&quot;&gt;The context&lt;/h2&gt;
&lt;p&gt;I don&#39;t know if I already shared that I work at &lt;a href=&quot;https://www.evaneos.fr&quot;&gt;Evaneos&lt;/a&gt;, a travel market place. Our job is to interconnect future travelers with travel agencies all over the world so they can benefit a trip tailor made by someone on site with a good knowledge of the destination. The agency&#39;s first job is to respond to a potential traveler request with a travel proposition after discussing the traveler&#39;s wishes.&lt;/p&gt;
&lt;p&gt;Lately, I&#39;ve been working on a rewrite of our routing system which for a given request selects the appropriate agency.&lt;/p&gt;
&lt;p&gt;A request can be in one of these 3 forms - here I keep the common vocabulary shared in the company:&lt;br&gt;
From itinerary: The traveler selects an example itinerary she is interested in. We route the request to the agency which created that itinerary.&lt;br&gt;
From agency: The traveler decides to talk directly with an agency. I bet you guessed which agency will receive the request.&lt;br&gt;
From scratch: The traveler indicates which destination she would like to travel to and we match her with the agency which can provide what we believe to be the best travel proposition according to the few information we have on what she wants to do.&lt;/p&gt;
&lt;h2 id=&quot;the-collision&quot; tabindex=&quot;-1&quot;&gt;The collision&lt;/h2&gt;
&lt;p&gt;During the rewrite we decided to provide some key insights to the people dealing with the settings of the from scratch algorithm.&lt;/p&gt;
&lt;p&gt;One of this insights is the sales rate for the requests made from scratch.&lt;/p&gt;
&lt;p&gt;During a meeting with business people when we were presenting that new tool someone noted that the sales rates shown were not the same as the one in the tool they were already using. As she pointed out, in the precedent tool what was considered from scratch were what the new tool was naming from scratch and from agency.&lt;/p&gt;
&lt;p&gt;Boom.&lt;/p&gt;
&lt;p&gt;We talked about it a few minutes but as this was not the point of the meeting we decided to postpone that discussion and to come back to it later.&lt;/p&gt;
&lt;h2 id=&quot;the-feeling&quot; tabindex=&quot;-1&quot;&gt;The feeling&lt;/h2&gt;
&lt;p&gt;This left me with the feeling of something wrong going on and as I kept working on the project that discussion was coming to my mind from time to time.&lt;br&gt;
It made no sense to me that what we were considering as from scratch could also include from agency request type.&lt;/p&gt;
&lt;p&gt;It took me a while, some sleepless nights, a lot of &lt;a href=&quot;https://twitter.com/giorgiosironi/status/752091661795229700&quot;&gt;showers&lt;/a&gt;, and a week of holidays to finally get it.&lt;/p&gt;
&lt;p&gt;We were simply not talking about the same thing. At all.&lt;/p&gt;
&lt;p&gt;The old tool shows the sales rate for a travel proposition - which is different thing than a request for a travel proposition - that isn&#39;t based on an preexisting itinerary, hence &amp;quot;from scratch&amp;quot;.&lt;br&gt;
It makes sense to take into account what we call from scratch and from agent in the routing system to compute the sales rate of propositions made from scratch.&lt;/p&gt;
&lt;p&gt;On one side we are talking about requests and on the other  about the resulting proposition.&lt;/p&gt;
&lt;h2 id=&quot;the-history-behind&quot; tabindex=&quot;-1&quot;&gt;The history behind&lt;/h2&gt;
&lt;p&gt;It&#39;s been only a few months since we started to separate the concept of request from the one of proposition. Before that, and still now because of old habits, we were referring to this concepts as &amp;quot;a quote&amp;quot;. This is a good example of Developper/Database Driven Design with all the &lt;a href=&quot;https://twitter.com/cyriux/status/857877532779139072&quot;&gt;associates problems&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After all what is the difference between a request and a proposition if not a status changing from one integer to the other in a table row?&lt;/p&gt;
&lt;p&gt;You can imagine the fun we had when we decided to track all propositions made for a given request. Yes, the first one isn&#39;t always the good one and there is some back and forth between the traveler and the agency.&lt;/p&gt;
&lt;p&gt;I truly believe that sharing a word for this two different concepts is one of the main reasons behind the misunderstanding.&lt;/p&gt;
&lt;h2 id=&quot;the-future-forward&quot; tabindex=&quot;-1&quot;&gt;The future forward&lt;/h2&gt;
&lt;p&gt;I think the solution is pretty straightforward. First, we really need to focus on using the appropriate names, request or proposition, when talking to each other.&lt;/p&gt;
&lt;p&gt;I think we should also, at least, rename &amp;quot;from scratch&amp;quot; to &amp;quot;from destination&amp;quot; in the routing context. This would prevent the confusion and is coherent with naming pattern used by the two others which represent the page on the website where the request was made.&lt;/p&gt;
&lt;p&gt;I said &amp;quot;at least&amp;quot; because I&#39;m not convinced we should tie the domain logic to the graphic interface and I believe we can come up with something better than a word such as meaningless as from is.&lt;/p&gt;
&lt;p&gt;As a start a request is not made from a destination but by someone who want to go to that destination. &amp;quot;A traveler makes a request for a trip to Italy&amp;quot;, &amp;quot;For&amp;quot;*1 isn&#39;t a great word either but the direction seems to be correct at least. This needs some more digging.&lt;/p&gt;
&lt;p&gt;Note that using &amp;quot;from&amp;quot; when talking about the proposition seems legitimate, the action direction is ok. &amp;quot;A proposition is built/prepared/conceived from an itinerary&amp;quot;, &amp;quot;a proposition is built/prepared/conceived from scratch&amp;quot;. Because an action takes a verb I think we would be better with including it in our discussions and in the code. This would add meaning and reduce the risk of another collision*2.&lt;/p&gt;
&lt;p&gt;The next step is to work on the naming with the business people, trying to find something that makes sense and provide meaning. This will probably require to use verbs alongside preposition but this is for the best and that will reduce the risk of future collisions.&lt;/p&gt;
&lt;p&gt;*1 I hesitated with going with &amp;quot;to&amp;quot; instead of &amp;quot;for&amp;quot;. None of them are really convincing. Looks like a good warning that they probably aren&#39;t the best suited for the job.&lt;/p&gt;
&lt;p&gt;*2 &amp;quot;That proposition built &lt;strong&gt;from&lt;/strong&gt; scratch and travel starts &lt;strong&gt;from&lt;/strong&gt; Paris&amp;quot;. Not the best example ever but you get my point.&lt;/p&gt;
&lt;p&gt;Hey! I&#39;m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; and if you feel like playing the &amp;quot;Jeu des mots&amp;quot; with me I promise we&#39;ll have some fun! You can comment below as well.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Using data provider to express business rules in test</title>
      <link href="https://blog.charlesdesneuf.com/articles/using-data-provider-to-express-business-rules-in-tests/"/>
      <updated>2017-09-25T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/using-data-provider-to-express-business-rules-in-tests/</id>
      <summary>Data provider to the rescue when it comes to test case naming.</summary>
      <content type="html">&lt;p&gt;When we write tests we want to achieve several things: ensure that the code works, create documentation, gain feedback about the ease of use of the system we are building and the quality of its design.&lt;/p&gt;
&lt;p&gt;Creating documentation with tests goes through good test case naming. We want the name of our test cases to express the behavior of the system under test, or even better, the business rules of the domain.&lt;/p&gt;
&lt;p&gt;As I was working around data providers with PhpUnit I noticed they offer a good opportunity to work toward that goal.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;DishTest&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPUnit_Framework_TestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;test_is_vegan_if_empty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isVegan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;test_is_not_vegan_if_it_contains_meat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;meat&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertFalse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isVegan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;test_is_not_vegan_if_it_contains_cheese&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;cheese&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertFalse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isVegan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;test_is_not_vegan_if_it_contains_egg&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;egg&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertFalse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isVegan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This test class fails at expressing the business rule. Every test case name is an example.&lt;/p&gt;
&lt;p&gt;Using a data provider we can manage to reduce the number of test cases and to express the business rule in a clearer way.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;DishTest&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPUnit_Framework_TestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;test_is_vegan_if_empty&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isVegan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;	 * @dataProvider nonVeganDishes&lt;br&gt;	 */&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;test_is_not_vegan_if_it_contains_non_vegan_aliments&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertFalse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isVegan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;nonVeganDishes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A dish with meat&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;meat&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A dish with cheese&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;cheese&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A dish with egg&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;egg&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Through the introduction of the data provider we made a separation between the business rule, in the test case name, and the supporting examples, provided by the data provider.&lt;/p&gt;
&lt;p&gt;We&#39;ve partly achieved our goal, the two remaining test cases convey way more information about the business rule. Still having the same rule expressed in a positive and a negative way feels really strange.&lt;/p&gt;
&lt;p&gt;We can keep the business rule expressed in the positive way if we modify the data provider in order to include the expected result.&lt;/p&gt;
&lt;p&gt;First, because we want to keep our tests as readable as we can, we should introduce two constants&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;IS_VEGAN&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;IS_NOT_VEGAN&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we can modify the tests cases and the data provider.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;DishTest&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPUnit_Framework_TestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;IS_VEGAN&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;IS_NOT_VEGAN&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;	 * @dataProvider dishes&lt;br&gt;	 */&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;test_is_vegan_if_it_contains_only_vegan_aliment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$isVegan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$isVegan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$dish&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isVegan&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;dishes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;An empty dish is vegan&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IS_VEGAN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A dish with fruits is vegan&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;fruits&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IS_VEGAN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A dish with meat is not vegan&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;meat&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IS_NOT_VEGAN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A dish with cheese is not vegan&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;cheese&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IS_NOT_VEGAN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;A dish with egg is not vegan&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;egg&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;IS_NOT_VEGAN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our test class now fully communicates the business rule as a domain expert would express it.&lt;/p&gt;
&lt;h2 id=&quot;listening-to-the-test&quot; tabindex=&quot;-1&quot;&gt;Listening to the test&lt;/h2&gt;
&lt;p&gt;Tests provide a good opportunity to reflect on the quality of the system design.&lt;/p&gt;
&lt;p&gt;When we take a look at the test name and the associated examples we can see the mismatch between them. We are talking about non vegan food in the name but are using aliment names such as meat or cheese. This is a hint we might want the aliment to convey the information about whether it&#39;s vegan or not. We then would be able to simplify our test with factory methods for vegan or non vegan aliment.&lt;/p&gt;
&lt;p&gt;Hey! I&#39;m on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; wher I sometimes talks about testing too ! You can comment below as well if you feel like doing so !&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Faster environment with xDebug and Docker</title>
      <link href="https://blog.charlesdesneuf.com/articles/faster-environment-with-xDebug-and-Docker/"/>
      <updated>2020-05-05T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/faster-environment-with-xDebug-and-Docker/</id>
      <summary>A few tips for a faster dev env with xDebug and Docker</summary>
      <content type="html">&lt;p&gt;I have to admit that I&#39;ve always been lazy when it came to set up xDebug as it felt tedious the few times I&#39;ve to do it. It made me an almost perpetual member of the &lt;code&gt;var_dump&lt;/code&gt; debug team.&lt;/p&gt;
&lt;p&gt;Nevertheless, the last time I had to set it up was more straightforward, thanks to PhpStorm, and I don&#39;t think the difficulty to set up is a valid excuse anymore.&lt;/p&gt;
&lt;p&gt;I then had a debugger…&lt;/p&gt;
&lt;p&gt;And I had a speed issue. My tests were running slower than before, and every request took way too much time. Using Docker on OSX didn&#39;t help at all.&lt;/p&gt;
&lt;p&gt;I found a few tips that helped me go back to the same state as before.&lt;/p&gt;
&lt;p&gt;So …&lt;/p&gt;
&lt;h2 id=&quot;tips-1%3A-do-not-start-xdebug&quot; tabindex=&quot;-1&quot;&gt;Tips 1: Do not start xDebug&lt;/h2&gt;
&lt;p&gt;Ahah… Yes, you can thank me for this one. The best way to avoid xDebug slowing everything down is by not having xDebug running. So, disable it by commenting the path to the extension in the .ini file loading it. Another idea is that if you have an xdebug.ini file in the PHP configuration directory, rename it to something else, xdebug.ini.back, for instance.&lt;/p&gt;
&lt;p&gt;This being said, how can we manage to start xDebug when needed?&lt;/p&gt;
&lt;h2 id=&quot;tips-2%3A-use-the-on-demand-phpstorm-mode-when-debugging-tests&quot; tabindex=&quot;-1&quot;&gt;Tips 2: Use the on-demand PhpStorm mode when debugging tests&lt;/h2&gt;
&lt;p&gt;As explained in the PhpStorm documentation, an &amp;quot;on-demand&amp;quot; mode is available. It will enable xDebug only when debugging tests, which will allow running your test at full speed most of the time.&lt;/p&gt;
&lt;p&gt;I&#39;ll let you dig in the documentation as it will be better explained than if I do it myself.&lt;/p&gt;
&lt;h2 id=&quot;tips-3%3A-use-the-on-demand-mode-without-phpstorm&quot; tabindex=&quot;-1&quot;&gt;Tips 3: Use the on-demand mode without PhpStorm&lt;/h2&gt;
&lt;p&gt;PhpStorm is not doing magic for the &amp;quot;on-demand&amp;quot; mode but taking advantage of the -d PHP CLI option. The -d allows defining INI entry in the PHP configuration. When starting the &amp;quot;on-demand&amp;quot; mode, PhpStorm adds &lt;code&gt;-d zend_extension=xdebug.so&lt;/code&gt; to the PHP CLI options, which enables the extension. You can do the same thing if you&#39;re running your tests without PhpStorm.&lt;/p&gt;
&lt;p&gt;As an example here is how you can run PhpUnit with xDebug activated:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;php &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;zend_extension&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;xdebug.so ./vendor/bin/phpunit&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You&#39;ll probably need to pass more options if you want to connect xDebug to a debugger client, so this not as convenient as running it through PhpStorm as it sets everything needed to work out the box. As an example, here are all the option passed by PhpStorm when I start tests in debug mode :&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;zend_extension&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;xdebug.so &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;xdebug.remote_enable&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;xdebug.remote_mode&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;req &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;xdebug.remote_port&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;9100&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;xdebug.remote_host&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;host.docker.internal&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is still a good option if you need xDebug without a client listening. I guess the primary use case would be for coverage reports.&lt;/p&gt;
&lt;h2 id=&quot;tips-4%3A-enable-xdebug-with-an-environment-variable-in-your-docker-container&quot; tabindex=&quot;-1&quot;&gt;Tips 4: Enable xDebug with an environment variable in your docker container&lt;/h2&gt;
&lt;p&gt;Going in the container and modifying files every time you need to enable or disable xDebug is cumbersome. I&#39;ve tested another option for the last few weeks, and it works very well.&lt;/p&gt;
&lt;p&gt;I&#39;ve modified my PHP Dockerfile to add an entry point script, which is run when the container starts and can access environment variables.&lt;/p&gt;
&lt;p&gt;The entry point script looks for an environment variable and decides to rename, or not, the xdebug.ini file in the PHP configuration directory.&lt;/p&gt;
&lt;p&gt;As an example, here is the &lt;code&gt;entrypoint.sh&lt;/code&gt; file I&#39;m using in a php5.6 container. You&#39;ll probably need to adapt it to suit your container.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DISABLE_XDEBUG&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DISABLE_XDEBUG&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/etc/php/5.6/mods-available/xdebug.ini&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;mv&lt;/span&gt; /etc/php/5.6/mods-available/xdebug.ini /etc/php/5.6/mods-available/xdebug.ini.back&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;EOF&lt;br&gt;⚠️  xDebug is disabled&lt;br&gt;EOF&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DISABLE_XDEBUG&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DISABLE_XDEBUG&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/etc/php/5.6/mods-available/xdebug.ini.back&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;mv&lt;/span&gt; /etc/php/5.6/mods-available/xdebug.ini.back /etc/php/5.6/mods-available/xdebug.ini&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$@&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;And the Dockefile is edited to include the entry point:&lt;br&gt;&lt;br&gt;COPY ./entrypoint.sh /&lt;br&gt;ENTRYPOINT &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/entrypoint.sh&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;CMD &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;php-fpm5.6&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The container can be started either with docker-compose or docker run and xDebug activated by setting the environment variable DISABLE_XDEBUG to false and disabled by setting it to true. When the variable is missing, xDebug will stay activated.&lt;/p&gt;
&lt;p&gt;We now have a convenient way to enable xDebug only when needed and save some precious time not waiting in front of loading pages. The only thing required is to change the value of the variable and restart the container.&lt;/p&gt;
&lt;h2 id=&quot;extra-tip&quot; tabindex=&quot;-1&quot;&gt;Extra tip&lt;/h2&gt;
&lt;p&gt;xDebug settings can be overridden using the XDEBUG_CONFIG environment variable, which means there is no need to update the xDebug config file every time you have to change a setting or if you and your teammates need different settings.&lt;/p&gt;
&lt;p&gt;The best example is probably the &lt;code&gt;remote_host&lt;/code&gt;. According to how you run docker, this setting could be a changing IP address or &lt;code&gt;host.docker.internal&lt;/code&gt; if you&#39;re using Docker for Mac.&lt;/p&gt;
&lt;p&gt;In order to set the remote host to &lt;code&gt;host.docker.internal&lt;/code&gt; the &lt;code&gt;XDEBUG_CONFIG&lt;/code&gt; should be set &lt;code&gt;remote_host=host.docker.internal&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I hope all these tips will help you improve the speed of your development environment.&lt;/p&gt;
&lt;p&gt;If you want to share some tips as well come say “Hi!” on &lt;a href=&quot;https://twitter.com/selrahcd&quot;&gt;Twitter&lt;/a&gt; or feel free to comment below.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Being lazy with Postman scripts and dynamic variables</title>
      <link href="https://blog.charlesdesneuf.com/articles/being-lazy-with-postman/"/>
      <updated>2020-09-28T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/being-lazy-with-postman/</id>
      <summary>Learn how to streamline API testing with Postman by automating UUID generation and variable reuse between requests. Eliminate manual copy-pasting and boost productivity with dynamic variables and scripts.</summary>
      <content type="html">&lt;p&gt;I&#39;m probably really late to the show as Postman is known to be a great tool when it comes to work with API. I&#39;ve never took the time to dig in what this tool can do but I recently had a workflow involving generating and copy-pasting UUIDs from responses to requests and decided to see if this could be automated and it turns out it can, relatively easily even.&lt;/p&gt;
&lt;h2 id=&quot;problem&quot; tabindex=&quot;-1&quot;&gt;Problem&lt;/h2&gt;
&lt;p&gt;Pretend we are working with an API allowing us to create an inventory for goods stored in fridge. The API is simple and offers two actions :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Registering a fridge to be tracked is done via a &lt;code&gt;PUT&lt;/code&gt; on &lt;code&gt;/fridges/{fridgeId}&lt;/code&gt; where &lt;code&gt;{fridgeId}&lt;/code&gt; is an UUID. The request body is not relevant for this example. A valid call will return a json response containing a field &lt;code&gt;id&lt;/code&gt; with the provided UUID.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Tracking that a product is stored in the fridge is done via a &lt;code&gt;PUT&lt;/code&gt; on &lt;code&gt;/fridges/{fridgeId}/products&lt;/code&gt; where &lt;code&gt;{fridgeId}&lt;/code&gt; is an UUID identifying an already registered fridge. The content of the request body and the response are not relevant for this example.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We would like to be able to generate an UUID when the registration route is called and avoid relying on another tool to get an UUID and then copy-past it in the URL input.&lt;/p&gt;
&lt;p&gt;We&#39;d also like to keep track of that UUID and be able to call the product stored tracking route with that newly created UUID without having to mess with input URL as well.&lt;/p&gt;
&lt;h2 id=&quot;generating-a-new-uuid&quot; tabindex=&quot;-1&quot;&gt;Generating a new UUID&lt;/h2&gt;
&lt;p&gt;Postman is able to deal with variables and even provides some &lt;a href=&quot;https://learning.postman.com/docs/writing-scripts/script-references/variables-list/&quot;&gt;dynamic variables&lt;/a&gt; which are replaced with some random values when the call is executed.&lt;br&gt;
One of them is &lt;code&gt;{{$guid}}&lt;/code&gt; and is replaced by an UUID.&lt;/p&gt;
&lt;p&gt;We can change the input field to make us of &lt;code&gt;{{$guid}}&lt;/code&gt;, the URL can be modified to include &lt;code&gt;/fridges/{{$guid}}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./input1.png&quot; alt=&quot;URL using &quot;&gt;&lt;/p&gt;
&lt;p&gt;Now everytime we will send the request a new UUID will be generated and used.&lt;/p&gt;
&lt;p&gt;First problem solved!&lt;/p&gt;
&lt;h2 id=&quot;reusing-the-uuid-for-storing-product&quot; tabindex=&quot;-1&quot;&gt;Reusing the UUID for storing product&lt;/h2&gt;
&lt;p&gt;We are able to access the newly created UUID looking at the response of the registering call. This is the UUID we want to reuse for our call to store a product.&lt;/p&gt;
&lt;p&gt;In order to automate the process and avoid copy-pasting the UUID from the response to the URL input field each and everytime we will take advantage of Postman scripting capability.&lt;/p&gt;
&lt;p&gt;Postman allows to &lt;a href=&quot;https://learning.postman.com/docs/writing-scripts/intro-to-scripts/&quot;&gt;run scripts before and after every call&lt;/a&gt;. Just below the URL input, alongside requests parameters you&#39;ll notice two tabs, Pre-request Script and Tests.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./tabs.png&quot; alt=&quot;View of the tabs&quot;&gt;&lt;/p&gt;
&lt;p&gt;The Tests tab can be used to execute tests after a request is made but can also be used to execute a script.&lt;/p&gt;
&lt;p&gt;By adding &lt;code&gt;pm.environment.set(&#39;fridgeId&#39;, pm.response.json().id)&lt;/code&gt; in the Tests tab input we instruct Postman to read the response, look for the &lt;code&gt;id&lt;/code&gt; field in the JSON and store it in a &lt;code&gt;fridgeId&lt;/code&gt; variable.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./test-input.png&quot; alt=&quot;Test input&quot;&gt;&lt;/p&gt;
&lt;p&gt;We are now able to use the &lt;code&gt;fridgeId&lt;/code&gt; for any subsequent call. The URL used for storing a product can be changed to &lt;code&gt;/fridges/{fridgeId}/products&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./input2.png&quot; alt=&quot;New url input with  variable&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;a-new-workflow&quot; tabindex=&quot;-1&quot;&gt;A new workflow&lt;/h2&gt;
&lt;p&gt;We&#39;ve greatly simplified our workflow :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Everytime we want to register a new fridge we only need to visit the request tab for fridge registration and click send&lt;/li&gt;
&lt;li&gt;We can store a product in the newly created fridge by clicking send on the other request tab as the request will use the fridge UUID directly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No more UUID generation using external tool and no more copy pasting !&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Passing data to session while testing in Elixir</title>
      <link href="https://blog.charlesdesneuf.com/articles/passing-data-to-session-in-elixir-tests/"/>
      <updated>2020-12-01T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/passing-data-to-session-in-elixir-tests/</id>
      <summary>It&#39;s not as hard as it seems to pass data to the session while testing a Phoenix application</summary>
      <content type="html">&lt;p&gt;I struggled a while when I tried to pass data to the session while setting up a Liveview test. I googled a lot and asked on the elixir slack for a solution, but everything I saw seemed &lt;a href=&quot;https://elixirforum.com/t/test-for-sessions-in-phoenix/2569&quot;&gt;quite complicated&lt;/a&gt;, and I didn&#39;t manage to make it work.&lt;/p&gt;
&lt;p&gt;I probably messed up something.&lt;/p&gt;
&lt;p&gt;I finally found relief and &lt;a href=&quot;https://paulhoffer.com/2018/03/22/easy-session-testing-in-phoenix-and-plug.html&quot;&gt;a blog post&lt;/a&gt; with a one liner that does it :&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token module class-name&quot;&gt;Plug&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init_test_session&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;conn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;user_id:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;a href=&quot;https://hexdocs.pm/plug/Plug.Test.html#init_test_session/2&quot;&gt;Plug documentation&lt;/a&gt; doesn&#39;t say much more.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Testing that a GenServer exited</title>
      <link href="https://blog.charlesdesneuf.com/articles/testing-that-a-genserver-exited/"/>
      <updated>2020-12-14T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/testing-that-a-genserver-exited/</id>
      <summary>We can test that a GenServer exited by monitoring its process</summary>
      <content type="html">&lt;p&gt;In the project I&#39;m working on, I need to keep a GenServer up for a while, but I don&#39;t want to keep it up forever as I want to free resources.&lt;br&gt;
The solution is to use the &lt;a href=&quot;https://hexdocs.pm/elixir/GenServer.html#module-timeouts&quot;&gt;timeout mechanism&lt;/a&gt; with an &lt;code&gt;handle_info(:timeout, _)&lt;/code&gt; callback stoping the loop.&lt;/p&gt;
&lt;p&gt;This is straightforward, but then another question came up: How should I test that ?&lt;/p&gt;
&lt;p&gt;After digging for a while I discovered that the solution is to monitor the GenServer process in the test. Once the GenServer process exits a &lt;code&gt;:DOWN&lt;/code&gt; message should be sent to the test process. The test can assert that the correct message was received.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;    test &lt;span class=&quot;token string&quot;&gt;&quot;MyGenServer exits after an_action&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fixtures &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token comment&quot;&gt;# Start the GenServer&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;start_supervised&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;MyGenServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token comment&quot;&gt;# Monitor the GenServer process&lt;/span&gt;&lt;br&gt;      ref &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;monitor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pid&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token comment&quot;&gt;# Start an action on the GenServer&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token module class-name&quot;&gt;MyGenServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;an_action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pid&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token comment&quot;&gt;# Assert that the :DOWN message for the GenServer process was received&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token comment&quot;&gt;# I decided to allow the message to arrive in a timeframe 10 times bigger than the allowed inactive period is &lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token comment&quot;&gt;# If the message isn&#39;t received by then the test is marked as failed&lt;/span&gt;&lt;br&gt;      assert_receive &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:DOWN&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;^&lt;/span&gt;ref&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:process&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pid&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:normal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;MyGenServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;allowed_inactive_period&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This solution applies to any type of Elixir process, not only to GenServers.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building an event-sourced game with Phoenix Liveview: Introduction</title>
      <link href="https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-intro/"/>
      <updated>2020-12-22T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-intro/</id>
      <summary>Introduction to the serie about building an event-sourced game with Phoenix Liveview</summary>
      <content type="html">&lt;p&gt;I’ve been playing with Elixir for a while now. I’ve &lt;a href=&quot;http://schtroumpsify.charlesdesneuf.com&quot;&gt;created a Twitter bot&lt;/a&gt;, but I wanted to explore further what Elixir and Phoenix could help build for real-time collaboration between people. I was also curious about building a domain model with a functional programming language.&lt;/p&gt;
&lt;p&gt;During the first French lockdown, I had the idea of a project that could be a good experiment: a game. As we had some fun playing &lt;a href=&quot;https://www.docteurpilule.com/&quot;&gt;Doctor Pilule&lt;/a&gt;, a French board game, I decided to build a digital version helping us to play with friends and family remotely.&lt;/p&gt;
&lt;h2 id=&quot;doctor-pilule&quot; tabindex=&quot;-1&quot;&gt;Doctor Pilule&lt;/h2&gt;
&lt;p&gt;The game is quite simple. Players are split into several teams, and each round, one player tries to make her other team members guess as many words as possible. The trick is that all players are given two uncommon handicaps, one spoken (ex: start all sentences by &amp;quot;I might sound crazy but...&amp;quot;) and one driving actions (ex: you think you are the Eiffel tower).&lt;br&gt;
The first team to reach 20 guessed words wins.&lt;/p&gt;
&lt;h2 id=&quot;why-this-game%3F&quot; tabindex=&quot;-1&quot;&gt;Why this game?&lt;/h2&gt;
&lt;p&gt;First, as I said, it’s fun to play, and we needed some fun this year. It also comes with some interesting challenges to deal with, which are making for a good experiment:&lt;br&gt;
it’s real-time&lt;br&gt;
it needs to deal with a timer&lt;br&gt;
all players don’t see the same thing at the same time (ex: the word to be guessed)&lt;br&gt;
it’s better if you play it with video&lt;br&gt;
game’s logic is complex enough to tinker with building a model with functional programming way without being overwhelming&lt;/p&gt;
&lt;h2 id=&quot;and-then%3F&quot; tabindex=&quot;-1&quot;&gt;And then?&lt;/h2&gt;
&lt;p&gt;I felt I could build something relatively quickly, helped by Phoenix Liveview and be able to play with friends after a few weeks.&lt;/p&gt;
&lt;p&gt;Of course, it didn’t work out as expected.&lt;/p&gt;
&lt;p&gt;As with many side projects, the fun part is not in delivering something but in learning. I spent a lot of time playing around, trying stuff, and being stuck and stubborn about solving problems not providing real value in the end—things we should probably avoid while at work; however, not so bad in a side project.&lt;/p&gt;
&lt;p&gt;I still didn’t reach the video part and still have things I’d like to play with. Nevertheless, I managed to build the game logic, display relevant information to players in real-time, event-sourced the system, and learn a few things along the way. This blog post series is about some of these learnings.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building an event-sourced game with Phoenix Liveview: Architecture</title>
      <link href="https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-architecture/"/>
      <updated>2020-12-23T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-architecture/</id>
      <summary>Overview of the architecture for DoctorP, an event-sourced game built on top of Phoenix LiveView</summary>
      <content type="html">&lt;p&gt;In this article, we’ll cover the macro architecture of the DoctorP project.&lt;/p&gt;
&lt;p&gt;The application is split into three parts: game logic, execution, and views.&lt;/p&gt;
&lt;h2 id=&quot;game-logic&quot; tabindex=&quot;-1&quot;&gt;Game logic&lt;/h2&gt;
&lt;p&gt;Game logic is pure business logic and is not concerned with the runtime properties of the system. Here seats the code expressing the game’s rules, written as much as possible using game terminology.&lt;/p&gt;
&lt;p&gt;It’s written following the functional core imperative shell pattern, which means that all the code here is pure.&lt;/p&gt;
&lt;p&gt;Game logic receives a command, the current game state, and everything else it might need to do its work.&lt;br&gt;
The game rules are applied, returning the result without producing any side effects. In a non-event-sourced system, the result would be the new game state. Here, as we are doing event sourcing, the code returns a list of events.&lt;/p&gt;
&lt;p&gt;Separating the game logic from the execution keeps our business logic free from runtime considerations. We can defer decisions on how we want our system to run to a later point. It also provides the ability to write fast-running unit tests without messing with processes.&lt;/p&gt;
&lt;h2 id=&quot;execution&quot; tabindex=&quot;-1&quot;&gt;Execution&lt;/h2&gt;
&lt;p&gt;In the execution part, we specify the runtime properties of the system.&lt;br&gt;
I’ve decided that a GenServer, &lt;code&gt;GameServer,&lt;/code&gt; will back each game room.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GameServer&lt;/code&gt; serves different purposes:&lt;br&gt;
It manages all commands and queries related to one game room. Having one process for each game room improves reliability. A game room can’t be blocked by something taking time in another game room, and in case of a crash, only one game room is affected.&lt;br&gt;
It stores all events produced by the actions taken in the game room. As this is a game and keeping all the data is not adding a lot of value, I’ve decided to store events in the process. One major downside to this solution is that we cannot get back to where the game was in case of a crash or server restart. Everything is lost.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GameServer&lt;/code&gt; are supervised using a dynamic supervisor. Because multiple &lt;code&gt;GameServer&lt;/code&gt; can be up simultaneously, a registry keeps the relation between game id and process id using the &amp;quot;via tuple mechanism.&amp;quot;&lt;/p&gt;
&lt;h2 id=&quot;views&quot; tabindex=&quot;-1&quot;&gt;Views&lt;/h2&gt;
&lt;p&gt;Views use Phoenix Liveview, which provides the real-time capabilities needed for the game with server-rendered HTML. No need to write a single line javascript.&lt;/p&gt;
&lt;p&gt;I’ve created a view for each phase of the game: waiting for players to arrive, playing the game, and displaying the result. An additional view seats on top of them and decides which one to show.&lt;br&gt;
I’ve had difficulties trying to use LiveView components in place of the game phase views, but I guess an alternative implementation could use them.&lt;/p&gt;
&lt;p&gt;All views use events produced by the game logic to decide what to show.&lt;br&gt;
When a view is mounted, it fetches all past events from the &lt;code&gt;GameServer,&lt;/code&gt; builds its own data structure of what to present, and renders. During the game events are published by the `GameServer’via the PubSub mechanism offered by Phoenix. Each view subscribes to the game channel and waits for new events to arrive. Once an event is received, the data structure is updated, and the view is modified accordingly.&lt;/p&gt;
&lt;p&gt;Here is a sketch of the macro architecture of the project, summing up what was said so far.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./doctorp_macro.png&quot; alt=&quot;Architecture sketch&quot;&gt;&lt;/p&gt;
&lt;p&gt;We’ll cover some parts of this project more in-depth in other articles of this series. Let me know if you’d like me to cover some specific aspects.&lt;/p&gt;
&lt;h2 id=&quot;bibliography&quot; tabindex=&quot;-1&quot;&gt;Bibliography&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.theerlangelist.com/article/spawn_or_not&quot;&gt;To spawn, or not to spawn ? - Saša Jurić&lt;/a&gt;: Saša’s blog post is one of the main sources of inspiration for the architecture of this project.&lt;/li&gt;
&lt;/ul&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building an event-sourced game with Phoenix Liveview: An event sourced model</title>
      <link href="https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-event-sourced-model/"/>
      <updated>2021-01-13T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-event-sourced-model/</id>
      <summary>This article covers the basis for an event sourced model with Elixir. This is the first part about the game logic of a game build on top Phoenix and Liveview.</summary>
      <content type="html">&lt;p&gt;This article on the series of building an event-sourced game with Elixir Phoenix and LiveView focuses on the game logic.&lt;/p&gt;
&lt;p&gt;It would be too long and probably unclear for the reader to cover everything in one article, so I’ve decided to start from the beginning and rebuild everything from the start, omitting details, sometimes being a bit vague, and lying a bit about the final implementation.&lt;/p&gt;
&lt;p&gt;In this first part about the game logic, we’ll see how I built some basis for an event-sourced model.&lt;/p&gt;
&lt;h2 id=&quot;an-event-sourced-model&quot; tabindex=&quot;-1&quot;&gt;An event-sourced model&lt;/h2&gt;
&lt;p&gt;Before discussing the architecture of the game logic, we need to understand what event sourcing is.&lt;/p&gt;
&lt;p&gt;As &lt;a href=&quot;https://martinfowler.com/eaaDev/EventSourcing.html&quot;&gt;summarized by Martin Fowler&lt;/a&gt;, it means that we &amp;quot;Capture all changes to an application state as a sequence of events.&amp;quot;&lt;/p&gt;
&lt;p&gt;In an event-sourced system, when something occurs, an event, or a list of events, is produced. It differs from a more traditionally designed system where the new state is returned.&lt;/p&gt;
&lt;p&gt;To get the current system state, we go through all past events and apply them one by one to rebuild the state incrementally.&lt;/p&gt;
&lt;p&gt;We now have the basic understanding we’ll need to continue. Still, I invite you to read Martin’s article if you want to get all advantages and difficulties related to this pattern.&lt;/p&gt;
&lt;p&gt;The game is event-sourced, which means that when something happens, events are produced and stored somewhere. Next time we want to act on the game, we’ll get all events and apply them one by one to rebuild the current state before applying the action.&lt;/p&gt;
&lt;h2 id=&quot;events&quot; tabindex=&quot;-1&quot;&gt;Events&lt;/h2&gt;
&lt;p&gt;As we’ve seen, events are a big part of the design: they describe what happened during the game.&lt;/p&gt;
&lt;p&gt;Events are designed as structures containing a map called &lt;code&gt;data&lt;/code&gt; that stores all information about an event.&lt;/p&gt;
&lt;p&gt;For instance, when a red card is dealt to a player, we need to know which card was given to whom.&lt;/p&gt;
&lt;p&gt;To avoid duplicating the same code in each event module, I came up with a module defining a macro we can reuse:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Event&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defmacro&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__using__&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_opts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;quote&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token keyword&quot;&gt;defstruct&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;data:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token keyword&quot;&gt;alias&lt;/span&gt; __MODULE__&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;        data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; data&lt;br&gt;               &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;        __MODULE__&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;__MODULE__&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;data:&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This macro declares the structure with the &lt;code&gt;data&lt;/code&gt; map and a &lt;code&gt;with&lt;/code&gt; function.&lt;br&gt;
The &lt;code&gt;with&lt;/code&gt; function has two clauses and can be called with a map or a keyword list to build the event with the appropriate data.&lt;/p&gt;
&lt;p&gt;Declaring an event becomes as easy as using the module:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;DealtRedCardToPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Event&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and were now able to create an event&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token module class-name&quot;&gt;DealtRedCardToPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; player&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;card:&lt;/span&gt; red_card&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;rebuilding-the-state&quot; tabindex=&quot;-1&quot;&gt;Rebuilding the state&lt;/h2&gt;
&lt;p&gt;As explained in the first part of the article, the current state is rebuilt from all the past events.&lt;/p&gt;
&lt;p&gt;I’ve created a &lt;code&gt;GameState&lt;/code&gt; module that provides a &lt;code&gt;build_state&lt;/code&gt; function. &lt;code&gt;build_state&lt;/code&gt; takes a list of events, &lt;code&gt;history&lt;/code&gt;, and rebuilds the state by calling &lt;code&gt;apply_event&lt;/code&gt; function with the state and the event being applied. The &lt;code&gt;apply_event&lt;/code&gt; function returns the state once the event is applied.&lt;/p&gt;
&lt;p&gt;Here is a simplified version with the state stored in a map and coping with &lt;code&gt;GameStarted&lt;/code&gt; and &lt;code&gt;PlayerJoinedTeam&lt;/code&gt; events.&lt;/p&gt;
&lt;p&gt;We’ll see in another article the code as it is, which is slightly more complex.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameState&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;build_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;history&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;br&gt;    defaultState &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;isStarted:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;players:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foldl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;history&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; defaultState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;isStarted:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;   &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;players:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;player &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;players&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now call &lt;code&gt;build_state&lt;/code&gt; with a history :&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GameState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# %{isStarted: false, players: []}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GameState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# %{isStarted: true, players: []}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Charles&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GameState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# %{isStarted: true, players: [&quot;Charles&quot;]}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Charles&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Paul&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GameState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# %{isStarted: true, players: [&quot;Paul&quot;, &quot;Charles&quot;]}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;producing-events&quot; tabindex=&quot;-1&quot;&gt;Producing events&lt;/h2&gt;
&lt;p&gt;We’ve just seen how we can rebuild the state from events, but where do events come?&lt;/p&gt;
&lt;p&gt;Events are the result of actions taken on the game.&lt;/p&gt;
&lt;p&gt;I’ve decided to use command objects to represent actions. Each command is an elixir structure containing the field necessary to describe the intention.&lt;/p&gt;
&lt;p&gt;For instance, here is the module for the command allowing to add a player with a given name:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Commands&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;AddPlayer&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defstruct&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;player_name:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command is dealt with by a &lt;code&gt;handle&lt;/code&gt; function with a clause for each event. Pattern matching does its job to find the appropriate one:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;AddPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;MarkWordAsGuessed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each clause body contains the game logic we want to apply based on the state and command passed as parameters. The result of the action is expressed as a list of events:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;AddPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player_name:&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As we can see, we need to build the game state prior to handling a command. For convenience, we can add a &lt;code&gt;handle_message&lt;/code&gt; function to our &lt;code&gt;GameState&lt;/code&gt; module, which takes the list of past events and the command. This function’s job is to rebuild the state and apply the command.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;history&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; command&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token function&quot;&gt;build_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;history&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;command&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, we have a basis for expressing our game logic with events and commands. In the next articles, we’ll see how I evolved this to deal with errors, describe the game’s state more clearly, and manage the timer.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building an event-sourced game with Phoenix Liveview: Handling errors</title>
      <link href="https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-handling-errors/"/>
      <updated>2021-01-20T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-handling-errors/</id>
      <summary>In this article we’ll see how I decided to deal with errors in the event-sourced model for a game.</summary>
      <content type="html">&lt;p&gt;In &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-event-sourced-model/&quot;&gt;the previous article&lt;/a&gt;, we’ve set-up everything required for a basic event-sourced model.&lt;/p&gt;
&lt;p&gt;We can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;record the decisions taken by the game logic in the form of events&lt;/li&gt;
&lt;li&gt;rebuild the current game state from a list of past events&lt;/li&gt;
&lt;li&gt;handle a command that triggers game logic&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So far, every command we’ve talked about was accepted by the game logic, processed, and events were produced. But sometimes, things are not going as expected, and the command is rejected.&lt;/p&gt;
&lt;p&gt;One example, we don’t accept several players with the same name. When a player tries to register, but another player is already there with the name selected, we need to refuse the command and obviously inform the player.&lt;/p&gt;
&lt;h2 id=&quot;rebuilding-the-state-and-our-memory&quot; tabindex=&quot;-1&quot;&gt;Rebuilding the state and our memory&lt;/h2&gt;
&lt;p&gt;First, as a hrefresher, let’s see how the state is built.&lt;/p&gt;
&lt;p&gt;Let’s pretend that a player named Jack wants to register.&lt;/p&gt;
&lt;p&gt;An &lt;code&gt;AddPlayer&lt;/code&gt; command is issued by the UI:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;history &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; list of past events&lt;br&gt;command &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;AddPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player_name:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Jack&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;action_result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GameState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handle_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;history&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; command&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The command is dispatched to the correct handler, the game logic produces a &lt;code&gt;PlayerJoinedTeam&lt;/code&gt; event we would find in the &lt;code&gt;action_result&lt;/code&gt; variable in the snippet above:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;AddPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player_name:&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This event joins the others in history, the list of past events.&lt;/p&gt;
&lt;p&gt;When we want to add a second player, we need to rebuild the state.&lt;/p&gt;
&lt;p&gt;As seen in the previous article, &lt;code&gt;GameState&lt;/code&gt; module contains an &lt;code&gt;apply_event&lt;/code&gt; function with a clause matching against &lt;code&gt;%PlayerJoinedTeam{}&lt;/code&gt; :&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt; &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;players:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;player &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;players&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a &lt;code&gt;PlayerJoinedTeam&lt;/code&gt; event is applied, the player is inserted into the list of players.&lt;/p&gt;
&lt;h2 id=&quot;hi!-i%E2%80%99m-jack!&quot; tabindex=&quot;-1&quot;&gt;Hi! I’m Jack!&lt;/h2&gt;
&lt;p&gt;Jack #1 is already in, some other player wants to join as well, and is named Jack too, Jack #2.&lt;/p&gt;
&lt;p&gt;A business rule disallows two players to share the same name, so we’ll need to reject Jack #2 attempt to register as Jack, and we’ll ask him to pick another name.&lt;/p&gt;
&lt;p&gt;The traditional way of dealing with errors in Elixir code is the OK/Error tuple.&lt;/p&gt;
&lt;p&gt;When something is going OK, the function returns a tuple in the form&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and when something failed&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; the_error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the path I decided to go with, but I made a slight twist and introduced an &lt;code&gt;ActionResult&lt;/code&gt; module.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;alias&lt;/span&gt; __MODULE__&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defstruct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;error:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;error:&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;error:&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;error:&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;error:&lt;/span&gt; other_error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;other_error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;error:&lt;/span&gt; other_error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; other_events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; other_events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This module is a glorified OK&#92;Error tuple but has functions that will come in handy later. They allow us to combine multiple &lt;code&gt;ActionResult&lt;/code&gt; together, concatenating events list when everything is right, returning an error when something went wrong.&lt;/p&gt;
&lt;p&gt;Armed with this module, let’s see how we can implement the business rule.&lt;/p&gt;
&lt;p&gt;First, we add a private &lt;code&gt;has_player_named&lt;/code&gt; function that returns true when we already know a player with the name given in arguments and false otherwise.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;has_player_named&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;players&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can now change the &lt;code&gt;handle&lt;/code&gt; function to&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;AddPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player_name:&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;cond&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;has_player_named&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:player_name_not_available&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a player tries to register with a name already taken by someone else, the function produces an &lt;code&gt;ActionResult&lt;/code&gt; with an error &lt;code&gt;:player_name_not_available&lt;/code&gt;. That same &lt;code&gt;ActionResult&lt;/code&gt; is then returned by the &lt;code&gt;GameState.handle_message&lt;/code&gt; function called with the command at the beginning of the process.&lt;/p&gt;
&lt;p&gt;That’s it!&lt;/p&gt;
&lt;p&gt;And that’s a lot... We’ll keep for a following article how to deal with the &lt;code&gt;ActionResult&lt;/code&gt; once it’s returned from &lt;code&gt;GameState.handle_message&lt;/code&gt;.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building an event-sourced game with Phoenix Liveview: Expressing domain concepts in the code</title>
      <link href="https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-expressing-domain-concepts-in-code/"/>
      <updated>2021-01-28T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-expressing-domain-concepts-in-code/</id>
      <summary>In this article, we’ll see how we can express domain concepts in the code, and reduce the size of the GameState module.</summary>
      <content type="html">&lt;p&gt;In the past few articles, we’ve put a lot of code in the &lt;code&gt;GameState&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;The module is responsible for state rebuilding and all the game logic. It knows all details of handling card decks, timer, scoreboard, players, ...&lt;/p&gt;
&lt;p&gt;The module size keeps growing, and it’s time to start splitting, keeping things maintainable.&lt;/p&gt;
&lt;p&gt;This article describes one way of splitting the code. In the next article, we’ll see the second one.&lt;/p&gt;
&lt;h2 id=&quot;splitting-up&quot; tabindex=&quot;-1&quot;&gt;Splitting up&lt;/h2&gt;
&lt;p&gt;So far, the &lt;code&gt;GameState&lt;/code&gt; module contains all the information in a map and all the logic to deal with adding a player, dealing card to a player, starting the timer, ...&lt;/p&gt;
&lt;p&gt;I decided to introduce several modules, each responsible for one concept in the game, &lt;code&gt;BlueDeck&lt;/code&gt;, &lt;code&gt;RedDeck&lt;/code&gt;, &lt;code&gt;Scoreboard&lt;/code&gt;, &lt;code&gt;Teams&lt;/code&gt;, &lt;code&gt;Timer&lt;/code&gt;, ...&lt;/p&gt;
&lt;p&gt;Adding modules helps in several aspects. First, each domain concept becomes more explicit and is encapsulated in one place. Secondly, it improves testability: we can test each concept independently without going through &lt;code&gt;GameState&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Let’s look at what changes when it comes to the scoreboard module but first, let’s get back to how we handle the score in our current system.&lt;/p&gt;
&lt;p&gt;When a game starts, we want every team score set to 0. Because we are in an event-sourced system, this logic occurs in the &lt;code&gt;apply_event&lt;/code&gt; function, more precisely in the clause matching with the &lt;code&gt;GameStarted&lt;/code&gt; event.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scores:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;duplicate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Introducing the &lt;code&gt;Scoreboard&lt;/code&gt; module allows to encapsulate the logic elsewhere and to express the intent more clearly:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scoreboard:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Scoreboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;for_teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, the &lt;code&gt;Scoreboard&lt;/code&gt; module looks like the following:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Scoreboard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defstruct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;scores:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;for_teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Scoreboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;scores:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;duplicate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;incrementing-team-score&quot; tabindex=&quot;-1&quot;&gt;Incrementing team score&lt;/h2&gt;
&lt;p&gt;When a team wins a point, we want to keep track of it by producing an event. The &lt;code&gt;Scoreboard&lt;/code&gt; module is an excellent place to put this logic. It makes for a more cohesive base code: everything related to tracking points stays inside the same module.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;increment_team_score&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scoreboard&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; team&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  current_score &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scoreboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scores&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; team&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;TeamGotAPoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; team&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;new_score:&lt;/span&gt; current_score &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;code&gt;GameState&lt;/code&gt; module, we can use this function whenever we need to increment a team score.&lt;/p&gt;
&lt;p&gt;Let’s take a detour and add a new clause for the &lt;code&gt;add&lt;/code&gt; function in the &lt;code&gt;ActionResult&lt;/code&gt; module we’ve defined in &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-handling-errors/&quot;&gt;the previous article&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other_events&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;other_events&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; other_events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now can add a list of events to an &lt;code&gt;ActionResult&lt;/code&gt; directly. It avoids needing to encapsulate every return value from the modules we’ll create from now on in an &lt;code&gt;ActionResult&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can now increment team score in the GameState module when we mark a word as guessed.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;MarkWordAsGuessed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;give_word_to_player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dictionary&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current_player&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Scoreboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;increment_team_score&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scoreboard&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;current_team_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’ve not simplified the code too much to show that sometimes multiple modules are called while handling a message. Here we want to increment a team score and give a new word to the current player.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ActionResult&lt;/code&gt; module makes it convenient to group events coming from several modules.&lt;/p&gt;
&lt;h2 id=&quot;rebuilding-scoreboard-state&quot; tabindex=&quot;-1&quot;&gt;Rebuilding Scoreboard state&lt;/h2&gt;
&lt;p&gt;We’ve seen that state contains the scoreboard in previous snippets, but we haven’t seen yet how the scoreboard state is maintained.&lt;/p&gt;
&lt;p&gt;We keep track of teams&#39; scores with events here as well. &lt;code&gt;Scoreboard&lt;/code&gt; can be event-sourced too.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Scoreboard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Scoreboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; scoreboard&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;TeamGotAPoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;data:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; team&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;new_score:&lt;/span&gt; team_score&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Scoreboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;scores:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;update_at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scoreboard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scores&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; team&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; _ &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; team_score &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And in &lt;code&gt;GameState&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;TeamGotAPoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scoreboard:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Scoreboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scoreboard&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;going-further&quot; tabindex=&quot;-1&quot;&gt;Going further&lt;/h2&gt;
&lt;p&gt;During this article’s redaction, an idea popped up in my head that I haven’t tried but is worth sharing.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GameState&lt;/code&gt;&#39;s &lt;code&gt;apply_event&lt;/code&gt; function now does very little. It takes the state and an event as parameters and dispatches to another module.&lt;/p&gt;
&lt;p&gt;Looking at the previous example close to other pieces of code we see an emerging pattern:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;TeamGotAPoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scoreboard:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Scoreboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scoreboard&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;DictionaryShuffled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;dictionary:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dictionary&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GaveWordToPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;dictionary:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dictionary&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We could avoid writing these pieces of code for each event by generalizing. Something like:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;br&gt;      module &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__struct__&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token module class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I haven’t tried this code, I don’t know if it works, nor it’s even a good idea.&lt;/p&gt;
&lt;p&gt;We’ve seen one way of breaking apart the &lt;code&gt;GameState&lt;/code&gt; module. In the next articles, we’ll see another decision I’ve made to reduce its size further down and improve communication around domain concepts.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building an event-sourced game with Phoenix Liveview: Making game states explicit</title>
      <link href="https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-making-game-states-explicit/"/>
      <updated>2021-02-04T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-making-game-states-explicit/</id>
      <summary>Type system can help express a system capabilty, what can be done, or prevent from misusage. This article shows how theses ideas were applied to express the game states.</summary>
      <content type="html">&lt;p&gt;In &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-expressing-domain-concepts-in-code/&quot;&gt;the last article&lt;/a&gt;, we’ve seen the first design choices I’ve taken to reduce the size of the &lt;code&gt;GameState&lt;/code&gt; module. Now, it’s time to see the second decision I’ve made.&lt;/p&gt;
&lt;p&gt;During this project, I wanted to experiment with expressing business logic with the type system in a functional language.&lt;/p&gt;
&lt;h2 id=&quot;business-logic-as-types&quot; tabindex=&quot;-1&quot;&gt;Business logic as types&lt;/h2&gt;
&lt;p&gt;While creating types to express business logic is something I’ve seen applied in OOP codebases, I’ve never played with that idea in a functional language.&lt;/p&gt;
&lt;p&gt;This idea is presented in &lt;a href=&quot;https://twitter.com/scottwlaschin&quot;&gt;Scott Wlaschin&lt;/a&gt;&#39;s book &lt;a href=&quot;https://fsharpforfunandprofit.com/books/&quot;&gt;Domain Modeling Made Functional&lt;/a&gt;. The book examples are written in F#, a language I haven’t got the chance to play with yet, but delivers interesting points applicable to Elixir nevertheless. It can also serve as a lightweight introduction to Domain Driven Design, and it’s worth a read.&lt;/p&gt;
&lt;p&gt;The main point Scott makes is that by &lt;a href=&quot;https://fsharpforfunandprofit.com/series/designing-with-types.html&quot;&gt;modeling using the type system&lt;/a&gt; we are able to create a robust and documented system expressed in the language of the business.&lt;/p&gt;
&lt;p&gt;One example of modeling with types is creating different types for different states. For instance, an email address could be validated or not validated yet, leading to the creation of &lt;code&gt;UnvalidatedEmailAddress&lt;/code&gt; and &lt;code&gt;ValidatedEmailAddress&lt;/code&gt; types, probably a workflow, able to transform an &lt;code&gt;UnvalidatedEmailAddress&lt;/code&gt; to a &lt;code&gt;ValidatedEmailAddress&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The game can be seen as a state machine, either waiting for players to register, waiting for a round to start, or in a round. Instead of having one type, materialized by an Elixir module with a struct containing many attributes to represent all states, as we have so far, Scott’s advice leads us to create three types, &lt;code&gt;BetweenRound&lt;/code&gt;, &lt;code&gt;InRound&lt;/code&gt;, &lt;code&gt;Waiting&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I admit having been lazy here and could probably have given more extended and more explicit names.&lt;/p&gt;
&lt;p&gt;The three modules:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# aliases and includes omitted&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defstruct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;teams:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defstruct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atom symbol&quot;&gt;:blue_deck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atom symbol&quot;&gt;:red_deck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atom symbol&quot;&gt;:teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atom symbol&quot;&gt;:scoreboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atom symbol&quot;&gt;:dictionary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;timer:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;round_time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;BetweenRound&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defstruct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atom symbol&quot;&gt;:blue_deck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atom symbol&quot;&gt;:red_deck&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atom symbol&quot;&gt;:teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atom symbol&quot;&gt;:scoreboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token atom symbol&quot;&gt;:dictionary&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Each module declares a structure with the precise information needed to do its job correctly. We can get rid of the permissive map we were using until now.&lt;/p&gt;
&lt;p&gt;We see some structure attributes are coming with default values. Every time the game arrives in that state, we want to reset that value.&lt;br&gt;
&lt;code&gt;timer&lt;/code&gt; in the &lt;code&gt;InRound&lt;/code&gt; state is probably the best example: when a round starts, the timer is always set to the expected round duration.&lt;/p&gt;
&lt;p&gt;We can also notice that &lt;code&gt;InRound&lt;/code&gt; and &lt;code&gt;BetweenRound&lt;/code&gt; states share many attributes as we want to preserve the game data while doing back and forth between these two states. When moving from &lt;code&gt;InRound&lt;/code&gt; to &lt;code&gt;BetweenRound&lt;/code&gt; we want to keep each team score.&lt;/p&gt;
&lt;h2 id=&quot;grouping-behaviors-into-states&quot; tabindex=&quot;-1&quot;&gt;Grouping behaviors into states&lt;/h2&gt;
&lt;p&gt;Now that we have three states, we can move functions related to each of them in the appropriate module.&lt;/p&gt;
&lt;p&gt;The handle function matching with the &lt;code&gt;MarkWordAsGuessed&lt;/code&gt; message can move to the &lt;code&gt;InRound&lt;/code&gt; module, the one dealing with &lt;code&gt;AddPlayer&lt;/code&gt; in &lt;code&gt;Waiting&lt;/code&gt;, and so on.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; waiting_room&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;AddPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player_name:&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;#...&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;MarkWordAsGuessed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;#...&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;An interesting thing to note here is that thanks to the introduction of structures, we can pattern match the current state. It makes it more straightforward which actions can be taken on each state.&lt;/p&gt;
&lt;p&gt;Furthermore, it will prevent any action made against the wrong state. If &lt;code&gt;MarkWordAsGuessed&lt;/code&gt; message were to be dispatched when we’re in &lt;code&gt;Waiting&lt;/code&gt; state, the application would crash.&lt;/p&gt;
&lt;p&gt;If we want to avoid crashing, we can add a clause, matching on all unmatched messages, that returns an error:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:action_not_allowed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also have to group the state mutation functions.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;teams:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PlayerLeftTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;teams:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;TeamGotAPoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scoreboard:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Scoreboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scoreboard&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, we pattern match on the current state structure, and functions return a structure.&lt;/p&gt;
&lt;h2 id=&quot;transition-between-states&quot; tabindex=&quot;-1&quot;&gt;Transition between states&lt;/h2&gt;
&lt;p&gt;We now have multiple states and cleaned up the &lt;code&gt;GameState&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;All examples I’ve shared so far show events that, when applied, stay in the same state. This is great, but we still need to transition from state to state, going from &lt;code&gt;Waiting&lt;/code&gt; to &lt;code&gt;BetweenRound&lt;/code&gt; and doing back and forth between &lt;code&gt;BetweenRound&lt;/code&gt; and &lt;code&gt;InRound&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The solution is simple. When needed, the &lt;code&gt;apply_event&lt;/code&gt; can return the structure of the next state.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;BetweenRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;teams:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scoreboard:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Scoreboard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;for_teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;BetweenRound&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;BetweenRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RoundStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;blue_deck:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;blue_deck&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;red_deck:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;red_deck&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;dictionary:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dictionary&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;teams:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;scoreboard:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scoreboard&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; module &lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RoundEnded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;BetweenRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;blue_deck:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;blue_deck&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;red_deck:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;red_deck&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;dictionary:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dictionary&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;teams:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token attr-name&quot;&gt;scoreboard:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;scoreboard&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I’m still puzzled about directly using another module structure in a different module. For instance, I don’t like that &lt;code&gt;Waiting&lt;/code&gt; module knows how to create the &lt;code&gt;Scoreboard&lt;/code&gt; for the &lt;code&gt;BetweenRound&lt;/code&gt; structure. It would probably be better to introduce a function in each module,  some sort of constructor, dealing with all the details. For lack of a name I liked, I’ve decided to keep the code as is. If you have an idea, feel free to tell me!&lt;/p&gt;
&lt;p&gt;Here is a schema of part of the state machine, with events leading to the same states and others occasioning transitions.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;state_transitions.png&quot; alt=&quot;State transitions schemas&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;what%E2%80%99s-left-to-gamestate&quot; tabindex=&quot;-1&quot;&gt;What’s left to GameState&lt;/h2&gt;
&lt;p&gt;We’ve moved all the game logic away from the &lt;code&gt;GameState&lt;/code&gt; module to the states modules. That improvement comes with an issue. We don’t have one single entry point to dispatch messages. We need to know the current state before select which module’s &lt;code&gt;handle&lt;/code&gt; function to call.&lt;/p&gt;
&lt;p&gt;We also still need someplace to put the logic of rebuilding the current state based on history.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GameState&lt;/code&gt; is the perfect place for this!&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Game&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameState&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;States&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; history&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;build_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;history&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;build_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;history&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;foldl&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;history&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Waiting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; command&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    state&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; command&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    state&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;__struct__&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This all that’s left in the module.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;dispatch_message&lt;/code&gt; function stays the same, ensuring that the state is rebuilt before handling the message.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;build_state&lt;/code&gt; is slightly changed to deal with states as structure instead of a map. The first value of the state is a &lt;code&gt;Waiting&lt;/code&gt; structure. Indeed, each game starts by waiting for players to registers.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;apply_event&lt;/code&gt;, we need to know which state module is the good one based on the state before calling &lt;code&gt;apply_event&lt;/code&gt; on it. This is done by the &lt;code&gt;module&lt;/code&gt; function, which reads the &lt;code&gt;__struct__&lt;/code&gt; key on the current state.&lt;/p&gt;
&lt;p&gt;That’s it!&lt;/p&gt;
&lt;p&gt;We’ve separated all the game logic from the event-sourcing and message handling one.&lt;/p&gt;
&lt;p&gt;Doing so, we’ve also improved the ability to understand our system: by looking at the list of modules, we’re able to know that the game can be in three states.&lt;/p&gt;
&lt;p&gt;Sure we’re still far away from everything that using the type system to express business logic. It’s only the first step. The same ideas could be applied to the modules we’ve seen in &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-expressing-domain-concepts-in-code/&quot;&gt;the last article&lt;/a&gt;. Typespecs would also improve the documentation and express what can’t be done by the system. It’s not something I’ve worked on yet. Maybe later then.&lt;/p&gt;
&lt;p&gt;In the next article, we’ll probably start looking at the runtime characteristics of the game.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building an event-sourced game with Phoenix Liveview: Game Server</title>
      <link href="https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-game-server/"/>
      <updated>2021-02-15T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-game-server/</id>
      <summary>Learn how to implement a game server using GenServer to store events and handle commands in event-sourced applications. Includes error handling and process communication patterns.</summary>
      <content type="html">&lt;p&gt;In the previous articles, we’ve looked at the design of the game’s rules. Now is an excellent time to focus on the runtime perspective of the application.&lt;/p&gt;
&lt;p&gt;We’ve seen that we can interact with a game via the &lt;code&gt;GameState&lt;/code&gt; module by providing a list of events and a command. We still need some way to store the events, and for this, we’ll use a GenServer.&lt;/p&gt;
&lt;h2 id=&quot;skeleton-of-gameserver&quot; tabindex=&quot;-1&quot;&gt;Skeleton of &lt;code&gt;GameServer&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;GameServer&lt;/code&gt; module is a &lt;code&gt;GenServer&lt;/code&gt; which allows keeping track of the events in a process.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameServer&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GenServer&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;start_link&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  game_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Keyword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:game_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;GenServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start_link&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;__MODULE__&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;process_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  game_id &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Keyword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:game_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;game_id:&lt;/span&gt; game_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;process_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:via&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Registry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;DoctorP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GamesRegistry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; game_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can start a &lt;code&gt;GameServer&lt;/code&gt; using the &lt;code&gt;start_link&lt;/code&gt; function, passing a keyword list containing a game id. The server’s initial state is a map containing the game id and an empty list of events.&lt;/p&gt;
&lt;p&gt;We also want to keep track of several games at the same time and to be able to interact with them using their game id. I’ve decided to use the via tuple method to link a game id and the server PID in a Registry. &lt;code&gt;process_name&lt;/code&gt; returns a tuple that indicates which registry and key to use to find the server.&lt;/p&gt;
&lt;h2 id=&quot;playing&quot; tabindex=&quot;-1&quot;&gt;Playing&lt;/h2&gt;
&lt;p&gt;The next thing to do is to be able to act on the &lt;code&gt;GameServer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;All actions follow the same patterns. For the sake of brevity, let’s focus solely on adding a player.&lt;/p&gt;
&lt;p&gt;First, I’ve introduced a client side function. It allows for an easy to use interface for other modules:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add_player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;GenServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;process_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:add_player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When called, &lt;code&gt;add_player&lt;/code&gt; issues a call to the server, using the process_name based on the game_id and asking to add a player with the provided player name.&lt;/p&gt;
&lt;p&gt;Its server-side counterpart is more interesting as it is responsible of dispatching a command to the game, storing the events, and replying to the caller.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:add_player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _from&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;AddPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player_name:&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, the &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-event-sourced-model/#producing-events&quot;&gt;&lt;code&gt;AddPlayer&lt;/code&gt; command&lt;/a&gt; is built and piped into to a private &lt;code&gt;handle_command&lt;/code&gt; function, alongside the state . The result of this call is, in turn, piped into a private &lt;code&gt;reply&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;Let’s dive into these!&lt;/p&gt;
&lt;h3 id=&quot;handling-the-command&quot; tabindex=&quot;-1&quot;&gt;Handling the command&lt;/h3&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;command&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  command&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;build_new_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;command&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;GameState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dispatch_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;command&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;build_new_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;events &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;handle_command&lt;/code&gt; is made of two parts, each one in its function.&lt;/p&gt;
&lt;p&gt;First, we use the &lt;code&gt;GameState&lt;/code&gt; module to dispatch the command. We can see that all previous events, currently stored in the state, are passed as a second parameter. It ensures the &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-event-sourced-model#producing-events/#rebuilding-the-state&quot;&gt;command handler knows the current state&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Secondly, the command dispatch result, an &lt;code&gt;ActionResult&lt;/code&gt; is piped into &lt;code&gt;build_new_state&lt;/code&gt;. Here the new state is created by appending the news events to the events currently stored. Note that &lt;code&gt;build_new_state&lt;/code&gt; returns a tuple containing the result of the dispatch and the new state.&lt;/p&gt;
&lt;h3 id=&quot;replying-to-the-caller&quot; tabindex=&quot;-1&quot;&gt;Replying to the caller&lt;/h3&gt;
&lt;p&gt;Once the command is dispatched, the game logic applied and, the new state created we need to reply to the caller.&lt;/p&gt;
&lt;p&gt;This is the job of the &lt;code&gt;reply&lt;/code&gt; function we’ve seen in the &lt;code&gt;handle_call&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;build_new_state&lt;/code&gt; returns a tuple containing an &lt;code&gt;ActionResult&lt;/code&gt; and the desired state after the command handling, which serves as a parameter for our new &lt;code&gt;reply&lt;/code&gt; function.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; new_state&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:reply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; new_state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we simply reply with the &lt;code&gt;:ok&lt;/code&gt; tuple and use the second part of the tuple for the new server’s state.&lt;/p&gt;
&lt;p&gt;The first part of the tuple seems useless by now, but it’s actually important.&lt;/p&gt;
&lt;p&gt;We’ve seen in &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-handling-errors/&quot;&gt;the article about error handling&lt;/a&gt; that part of &lt;code&gt;ActionResult&lt;/code&gt;&#39;s job is to keep track of errors. So far, &lt;code&gt;reply&lt;/code&gt; only handles the positive cases.&lt;/p&gt;
&lt;p&gt;I’ve decided to communicate errors to the caller by responding with a tuple starting with the &lt;code&gt;:error&lt;/code&gt; atom and containing the error provided by the game.&lt;/p&gt;
&lt;p&gt;To do this we need to add a clause for the &lt;code&gt;reply&lt;/code&gt; function matching with &lt;code&gt;ActionResult&lt;/code&gt; containing errors:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;error:&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; new_state&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:reply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; new_state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;GameServer&lt;/code&gt; can now deal with both positive outputs, made of new events and errors, and communicates them to callers.&lt;/p&gt;
&lt;p&gt;There are still exciting things to say about the &lt;code&gt;GameServer&lt;/code&gt;, from how it communicates with the Liveviews, to stopping them and more. These are the things we’ll cover in the next articles!&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building an event-sourced game with Phoenix Liveview: Acting on the game from the views</title>
      <link href="https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-acting-on-the-game-from-the-views/"/>
      <updated>2021-02-22T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-acting-on-the-game-from-the-views/</id>
      <summary>This article shows how the views send both simple commands and commands based on forms to the game server, and handle errors.</summary>
      <content type="html">&lt;p&gt;In the last article, we’ve seen the &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-game-server/&quot;&gt;GameServer implementation&lt;/a&gt;. It’s now time to look at the views, specifically how players interact with the game.&lt;/p&gt;
&lt;h2 id=&quot;starting-the-game-from-the-liveview&quot; tabindex=&quot;-1&quot;&gt;Starting the game from the LiveView&lt;/h2&gt;
&lt;p&gt;I decided to use LiveViews as they are convenient for real-time interactions without requiring any Javascript code.&lt;/p&gt;
&lt;p&gt;In this article, we’ll focus on the LiveView used for players to register as it exhibits handling form, sending messages to the &lt;code&gt;GameServer&lt;/code&gt;, and handling potential errors.&lt;/p&gt;
&lt;p&gt;Let’s first create a new module for our LiveView. When the view is mounted, the &lt;code&gt;game_id&lt;/code&gt; available in the route parameters is stored in the state.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorPWeb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;WaitingRoomLive&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;game_id:&lt;/span&gt; game_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;game_id:&lt;/span&gt; game_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;  socket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; socket&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The simplest case to handle in this state is handling the click on the &amp;quot;Start game&amp;quot; button. The LiveView receives an event without any parameters and sends a message to the &lt;code&gt;GameServer&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token attribute variable&quot;&gt;@impl&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;start_playing&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;GameServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start_playing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Thanks to the client function we’ve added to &lt;code&gt;GameServer&lt;/code&gt; and being able to find it using the game ID, the implementation is easy. When a &lt;code&gt;start_playing&lt;/code&gt; event is received by the LiveView process a call is issued to &lt;code&gt;GameServer.start_playing(socket.assigns.game_id)&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;registering-a-player-from-the-liveview&quot; tabindex=&quot;-1&quot;&gt;Registering a player from the LiveView&lt;/h2&gt;
&lt;p&gt;Registering a player is more complex because it involves a form for the player to specify her name.&lt;/p&gt;
&lt;p&gt;Following ideas from &lt;a href=&quot;https://www.amberbit.com/blog/2017/12/27/ecto-as-elixir-data-casting-and-validation-library/&quot;&gt;this article&lt;/a&gt;, I managed to use Ecto as a validation library to create a form that is not tied to a database.&lt;/p&gt;
&lt;p&gt;We declare a module describing the form:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Ecto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Schema&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Ecto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Changeset&lt;/span&gt;&lt;br&gt;&lt;br&gt;  embedded_schema &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:player_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;alias&lt;/span&gt; __MODULE__&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;register_player&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;&#92;&#92;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    register_player&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cast&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:player_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validate_required&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:player_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;validate_length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:player_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;min:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_changes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;changeset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;Ecto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply_changes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;changeset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once this module is created, we can use the form in the LiveView.&lt;br&gt;
In the &lt;code&gt;mount&lt;/code&gt; function we need to add the form changeset.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;game_id:&lt;/span&gt; game_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;changeset:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And modify the template to display the form:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&amp;lt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; form_for &lt;span class=&quot;token attribute variable&quot;&gt;@changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;phx_submit:&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:register_player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;id:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;player_registration_form&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; f &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; label f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:player_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text_input f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:player_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;autofocus:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;placeholder:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;What’s your name?&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; error_tag f&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:player_name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; submit &lt;span class=&quot;token string&quot;&gt;&quot;Register&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;phx_disable_with:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Registering...&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;form&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, the next step is to handle the form by creating a clause for &lt;code&gt;handle_event&lt;/code&gt; matching the &lt;code&gt;register_player&lt;/code&gt; event.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;register_player&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;register_player&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; register_player&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  changeset &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;register_player&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:action&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt; with &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; changeset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;valid?&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                   registration &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply_changes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;changeset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                   &lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GameServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add_player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;player_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;br&gt;    _ &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; changeset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the first part of the function, a new changeset for the form is created based on the form’s data.&lt;/p&gt;
&lt;p&gt;If the changeset is valid, the changes are applied to an empty &lt;code&gt;RegisterUser&lt;/code&gt; structure. We access data from that structure to call &lt;code&gt;GameServer.add_player&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;If anything is not going as expected, we enter the &lt;code&gt;else&lt;/code&gt; part of the &lt;code&gt;with&lt;/code&gt; structure. The changeset is given back to the view, allowing to display form validation errors to the player.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;name_blank_error.jpg&quot; alt=&quot;Name can’t be blank error displayed&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;displaying-errors-from-the-gameserver&quot; tabindex=&quot;-1&quot;&gt;Displaying errors from the &lt;code&gt;GameServer&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Another type of error is the ones returned by the GameServer when it cannot handle a command.&lt;/p&gt;
&lt;p&gt;Let’s take an example with the registration process. As seen in &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-handling-errors/&quot;&gt;the article about errors&lt;/a&gt;, we disallow two players to share the same name. If a player tries to register with a name taken by another player, an &lt;code&gt;:player_name_not_available&lt;/code&gt; is returned by &lt;code&gt;GameServer.add_player&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Sure, we could add some logic in the &lt;code&gt;WaitingRoomLive&lt;/code&gt; LiveView module to keep track of all registered players and prevent form submission if needed, but what if two players try to register with the same still available name at the exact same moment?&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;GameServer&lt;/code&gt;, as the source of truth, will handle one command after the other and refuse the second one.&lt;/p&gt;
&lt;p&gt;Let’s see how to handle this.&lt;/p&gt;
&lt;p&gt;First, we add the &lt;code&gt;add_player_name_already_taken_error&lt;/code&gt; to the &lt;code&gt;RegisterPlayer&lt;/code&gt; module, the one responsible for the registration form.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;# ....&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attribute variable&quot;&gt;@name_already_taken_error_message&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;This name is already taken by another player&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt; &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add_player_name_already_taken_error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;changeset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    changeset&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add_error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:player_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attribute variable&quot;&gt;@name_already_taken_error_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function adds an error for the &lt;code&gt;:player_name&lt;/code&gt; field to the changeset.&lt;/p&gt;
&lt;p&gt;Then, we can modify the &lt;code&gt;with&lt;/code&gt; structure we’ve seen previously to use that function when needed.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;with &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; changeset&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;valid?&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                 registration &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply_changes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;changeset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                 &lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GameServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add_player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;player_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; registration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;player_name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:player_name_not_available&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add_player_name_already_taken_error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;changeset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  _ &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; changeset&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If &lt;code&gt;GameServer.add_player&lt;/code&gt; returns the &lt;code&gt;{:error, :player_name_not_available}&lt;/code&gt; error tuple, we assign a modified changeset with the error to the socket.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;name_taken_error.jpg&quot; alt=&quot;Name already taken error displayed&quot;&gt;&lt;/p&gt;
&lt;p&gt;That’s it for this article. We’ve covered all needed for players to act against the game. In the next article, &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-building-views-states-and-reacting-to-changes/&quot;&gt;we’ll see how the view constructs its state from the game’s state&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building an event-sourced game with Phoenix Liveview: Building the view’s states from the events and reacting to changes</title>
      <link href="https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-building-views-states-and-reacting-to-changes/"/>
      <updated>2021-03-01T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-building-views-states-and-reacting-to-changes/</id>
      <summary>Learn how to build LiveView projections in Phoenix using event-sourcing. Discover how to rebuild view states from event history and update views in real-time using PubSub for seamless user experiences.</summary>
      <content type="html">&lt;p&gt;In &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-acting-on-the-game-from-the-views/&quot;&gt;the last article&lt;/a&gt;, we’ve seen how the view communicates with the &lt;code&gt;GameServer&lt;/code&gt; to send commands and act on the game. This article focuses on how the views construct their states.&lt;/p&gt;
&lt;p&gt;The game being event-sourced its state is derived from the list of past events, and view states are not going to be different. In CQRS-ES language, they are called projections.&lt;/p&gt;
&lt;p&gt;The first mode of building the view is rebuilding. When the view is loaded, we want it to represent the current state of the game. For example, when the game is waiting to start, some players might join while others are still not on the view. When a new player arrives, we want to display the list of all previously registered players.&lt;/p&gt;
&lt;p&gt;The second mode is the running mode of building the view: when something occurs in the game, we want displayed information to change if necessary.&lt;/p&gt;
&lt;h2 id=&quot;rebuilding-the-view&quot; tabindex=&quot;-1&quot;&gt;Rebuilding the view&lt;/h2&gt;
&lt;p&gt;First, let’s introduce the &lt;code&gt;EventSourcedLiveView&lt;/code&gt; module that will encapsulate logic commons to all event-based views.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorPWeb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;EventSourcedLiveView&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token attribute variable&quot;&gt;@callback&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;view_init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Phoenix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;LiveView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsigned_params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:not_mounted_at_router&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;              session &lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;              socket &lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Phoenix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;LiveView&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Socket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;integer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defmacro&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;__using__&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;opts &lt;span class=&quot;token operator&quot;&gt;&#92;&#92;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;quote&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorPWeb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:live_view&lt;/span&gt;&lt;br&gt;&lt;br&gt;      &lt;span class=&quot;token attribute variable&quot;&gt;@impl&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; init_state&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;view_init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;        socket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; socket&lt;br&gt;                 &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;init_state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The module declares a macro that allows creating a specific type of LiveView, the event-sourced ones.&lt;/p&gt;
&lt;p&gt;When the view is mounted, the &lt;code&gt;init_view&lt;/code&gt; function is called and is expected to return a tuple containing the game ID and a map used as the initial state.&lt;/p&gt;
&lt;p&gt;We can remove the &lt;code&gt;mount&lt;/code&gt; function from the &lt;code&gt;WaitingRoomLive&lt;/code&gt; we’ve seen in the previous article and instead write:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorPWeb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;WaitingRoomLive&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;DoctorPWeb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;EventSourcedLiveView&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;view_init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;game_id:&lt;/span&gt; game_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      game_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token attr-name&quot;&gt;changeset:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token attr-name&quot;&gt;game_id:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To rebuild the state from past events, we’ll modify the &lt;code&gt;EventSourcedLiveView&lt;/code&gt; module.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;mount&lt;/code&gt; function fetches all known events from the &lt;code&gt;GameServer&lt;/code&gt; and passes them to &lt;code&gt;set_states&lt;/code&gt;, with the &lt;code&gt;init_state&lt;/code&gt; we have from the previous call to &lt;code&gt;init_view&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;events &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GameServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;socket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; socket&lt;br&gt;         &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;init_state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;set_state&lt;/code&gt; function goes through all events and looks for a clause of the &lt;code&gt;apply_event&lt;/code&gt; matching each one of them. This is the same mechanism as for rebuilding the state we’ve seen when &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-event-sourced-model&quot;&gt;introducing event-sourced systems&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;set_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; history&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;history&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, let’s see how we can display the teams as an example. We’ve seen before that &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-handling-errors&quot;&gt;the game dispatches a &lt;code&gt;PlayerJoinedTeam&lt;/code&gt; event when a player registers&lt;/a&gt;, and we can use these events to achieve what we want.&lt;/p&gt;
&lt;p&gt;First, we add an empty list as the list of teams in the initial state:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;view_init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;game_id:&lt;/span&gt; game_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      game_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token attr-name&quot;&gt;changeset:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;changeset&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RegisterPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token attr-name&quot;&gt;game_id:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token attr-name&quot;&gt;teams:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we add the &lt;code&gt;apply_event&lt;/code&gt; function clause matching with the &lt;code&gt;PlayerJoinedTeam&lt;/code&gt; event.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;data:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; player&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; team_id&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  state&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;put&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add_player_to_teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; player&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; team_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add_player_to_teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; player&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; team_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  team &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; teams&lt;br&gt;         &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;team_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;         &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; t &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert_at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; player&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; team_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert_at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; team_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; team&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;replace_at&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; team_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; team&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function, alongside the private &lt;code&gt;add_player_to_teams&lt;/code&gt; function, adds the player to the indicated team in the list in the LiveView state.&lt;/p&gt;
&lt;h2 id=&quot;modifying-the-state-while-the-game-is-running&quot; tabindex=&quot;-1&quot;&gt;Modifying the state while the game is running&lt;/h2&gt;
&lt;p&gt;We’ve seen just above how the view state is built from the already known events when the view is mounted, but we haven’t touched the funnier part yet, modifying the view when something occurs in the game. We’ll see this in that second section.&lt;/p&gt;
&lt;p&gt;To communicate between the &lt;code&gt;GameServer&lt;/code&gt; and the LiveViews, we’ll use the Phoenix PubSub system.&lt;/p&gt;
&lt;p&gt;We introduce a &lt;code&gt;GamePubSub&lt;/code&gt; module. Its role is to make it convenient to publish and subscribe to message for a specific game.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GamePubSub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;Phoenix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PubSub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;DoctorP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PubSub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;game_topic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;Phoenix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PubSub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;broadcast&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;DoctorP&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PubSub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;game_topic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;game_topic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;game_&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;#{&lt;/span&gt;game_id&lt;span class=&quot;token delimiter punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, the &lt;code&gt;GameServer&lt;/code&gt; we’ve worked on &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-game-server&quot;&gt;in a previous article&lt;/a&gt; can be modified to dispatch events:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GameServer&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;#...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;command&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  command&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;build_new_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; event &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GamePubSub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;publish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  result&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;handle_command&lt;/code&gt; now pipes the result of &lt;code&gt;dispatch_command&lt;/code&gt; into &lt;code&gt;dispatch_events&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dispatch_events&lt;/code&gt; calls the &lt;code&gt;publish&lt;/code&gt; function of the newly created &lt;code&gt;GamePubSub&lt;/code&gt; module for each event. The function returns its first parameters, the &lt;code&gt;ActionResult&lt;/code&gt; coming from &lt;code&gt;dispatch_command&lt;/code&gt;, which allows continuing to pipe into &lt;code&gt;build_new_state&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Every time a command is dispatched and the game produces events, the &lt;code&gt;GameServer&lt;/code&gt; broadcasts them via PubSub. That means we can subscribe and wait for events.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;EventSourcedLiveView&lt;/code&gt; module will precisely do this.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;mount&lt;/code&gt; function is changed to&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; initial_state&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;view_init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; session&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;connected?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;GamePubSub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  socket &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; socket&lt;br&gt;           &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;set_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;initial_state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GameServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game_id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the view is connected, we subscribe for new events about the game.&lt;/p&gt;
&lt;p&gt;For every message broadcasted by the PubSub system, the &lt;code&gt;handle_info&lt;/code&gt; function is called and calls the&lt;code&gt; apply_event&lt;/code&gt; function, hoping that a matching clause for the event exists. In that case, the state is modified and assigned to the socket.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token attribute variable&quot;&gt;@impl&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;event&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;          &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;clean_assigns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; socket &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;assign&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;clean_assigns&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; clean_assigns&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pop!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;assigns&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:flash&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  clean_assigns&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A note here: I had to add the &lt;code&gt;clean_assigns&lt;/code&gt; function to prevent LiveView assigns&#39; error for the &lt;code&gt;:flash&lt;/code&gt; reserved keys.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;apply_event&lt;/code&gt; function called in &lt;code&gt;handle_info&lt;/code&gt; is the same one used when the view is mounted. It means the logic we created for building the list of teams is already working!&lt;/p&gt;
&lt;p&gt;That’s it! We’ve seen everything needed for our views to display the correct information when they are mounted or when something occurs in the game.&lt;/p&gt;
&lt;p&gt;In the next article, we’ll see the part I enjoyed the most coding in this project, as it forced me to rethink some pieces and improve the design, which is &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-building-decrementing-the-timer/&quot;&gt;dealing with the timer&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building an event-sourced game with Phoenix Liveview: Decrementing the timer</title>
      <link href="https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-building-decrementing-the-timer/"/>
      <updated>2021-03-08T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-building-decrementing-the-timer/</id>
      <summary>This article shows how to add a timer in an event-sourced game while keeping game logic decoupled from architecture concern using the send message to self pattern.</summary>
      <content type="html">&lt;p&gt;One thing that always comes with challenges in applications is time. In Docteur Pilule, we have an instance of time in the form of a timer: a round is 40 seconds long.&lt;/p&gt;
&lt;p&gt;When I started coding the game, I wanted to display the decrementing counter, changing every second, to players.&lt;/p&gt;
&lt;p&gt;In this article, we’ll see that thanks to Elixir, Liveview, and the design choices made so far, it’s pretty easy to do.&lt;/p&gt;
&lt;h2 id=&quot;sending-a-message-to-self&quot; tabindex=&quot;-1&quot;&gt;Sending a message to self&lt;/h2&gt;
&lt;p&gt;Here I propose a solution based on what Greg Young describes as &amp;quot;sending a message to self&amp;quot;.&lt;/p&gt;
&lt;p&gt;The idea is that a component passes a message to a delivery mechanism with a date it would like to receive the message and goes back to its own business. When it’s time, the delivery mechanism sends the message to the component to be handled as any other message.&lt;/p&gt;
&lt;h2 id=&quot;future-message-container&quot; tabindex=&quot;-1&quot;&gt;Future message container&lt;/h2&gt;
&lt;p&gt;We’ve seen in a previous article that &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-handling-errors/#hi-im-jack&quot;&gt;every action returns an &lt;code&gt;ActionResult&lt;/code&gt; structure&lt;/a&gt;. The &lt;code&gt;ActionResult&lt;/code&gt; module is the mean of communication between the game rules and the game server. It can be extended to handle future messages.&lt;/p&gt;
&lt;p&gt;First, we add a new key in the structure, &lt;code&gt;scheduled_messages&lt;/code&gt;. That key points to a list of tuples containing a delay in milliseconds and a message.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;defstruct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;scheduled_messages:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token attr-name&quot;&gt;error:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;nil&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can then complete the module with functions helping us deal with combining several &lt;code&gt;ActionResults&lt;/code&gt; that may contain scheduled messages.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; scheduled_messages&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scheduled_messages:&lt;/span&gt; scheduled_messages&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scheduled_messages:&lt;/span&gt; scheduled_messages&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; other_events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scheduled_messages:&lt;/span&gt; other_scheduled_messages&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; other_events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scheduled_messages:&lt;/span&gt; scheduled_messages &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; other_scheduled_messages&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scheduled_messages:&lt;/span&gt; scheduled_messages&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other_events&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;when&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;is_list&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;other_events&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events &lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt; other_events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scheduled_messages:&lt;/span&gt; scheduled_messages&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;decrementing-the-timer&quot; tabindex=&quot;-1&quot;&gt;Decrementing the timer&lt;/h2&gt;
&lt;p&gt;Choosing to start, decrement, or stop the timer is a game rule and thus resides in the application’s game logic part. &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-expressing-domain-concepts-in-code&quot;&gt;As seen previously&lt;/a&gt;, each concept in the game gets its module with functions acting as commands and the &lt;code&gt;apply_event&lt;/code&gt; callback returning a new state when an event is applied.&lt;/p&gt;
&lt;p&gt;Even if the &lt;code&gt;Timer&lt;/code&gt; module needs to deal with scheduled messages, it is no different than other modules.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Timer&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt; &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attribute variable&quot;&gt;@round_time&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token attribute variable&quot;&gt;@one_second&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:timer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;start_time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token module class-name&quot;&gt;RoundTimeStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;          &lt;span class=&quot;token attr-name&quot;&gt;remaining_time:&lt;/span&gt; &lt;span class=&quot;token attribute variable&quot;&gt;@round_time&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attribute variable&quot;&gt;@one_second&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Tick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;decrement_time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RoundEnded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;decrement_time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;current_time&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    time &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; current_time &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RoundTimeTicked&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;remaining_time:&lt;/span&gt; time&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attribute variable&quot;&gt;@one_second&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Tick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_time&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RoundTimeTicked&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;data:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;remaining_time:&lt;/span&gt; remaining_time&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt; remaining_time&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_time&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RoundTimeStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;data:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;remaining_time:&lt;/span&gt; remaining_time&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt; remaining_time&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The module defines two module attributes, &lt;code&gt;@round_time&lt;/code&gt; and &lt;code&gt;@one_second&lt;/code&gt;, respectively describing the round duration in seconds and one second expressed in milliseconds.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;start_time&lt;/code&gt; function returns an &lt;code&gt;ActionResult&lt;/code&gt; containing a &lt;code&gt;RoundStarted&lt;/code&gt; event with a remaining time set to &lt;code&gt;@round_time&lt;/code&gt;. The &lt;code&gt;ActionResult&lt;/code&gt; also contains a scheduled message, &lt;code&gt;%Tick{}&lt;/code&gt;, programmed to be received in one second.&lt;/p&gt;
&lt;p&gt;Similarly, &lt;code&gt;decrement_time&lt;/code&gt; decrements the time, produces a &lt;code&gt;RoundTimeTicked&lt;/code&gt; event, and schedules a &lt;code&gt;%Tick{}&lt;/code&gt; for one second later.&lt;/p&gt;
&lt;p&gt;If the timer’s value is &lt;code&gt;1&lt;/code&gt;, the first clause of &lt;code&gt;decrements_time&lt;/code&gt; is selected. It produces an &lt;code&gt;%RoundEnded{}&lt;/code&gt; event and doesn’t schedule a &lt;code&gt;%Tick{}&lt;/code&gt; as there is no need to decrement the timer anymore.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;apply_events&lt;/code&gt; for &lt;code&gt;%RoundTimeTicked{}&lt;/code&gt; and &lt;code&gt;%RoundTimeStarted&lt;/code&gt; return the remaining time contained in the event as the new timer state.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;InRound&lt;/code&gt; module, &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-making-game-states-explicit/#business-logic-as-types&quot;&gt;which encapsulates all game behaviors during a round&lt;/a&gt;,  stays simple.&lt;/p&gt;
&lt;p&gt;When a player starts a round, the timer starts, and a word is distributed.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;start_round&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;start_time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Dictionary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;give_word_to_player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;dictionary&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Teams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;current_player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;teams&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a &lt;code&gt;%Tick{}&lt;/code&gt; message is received, the timer is decremented.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; game&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Tick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;decrement_time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;game&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The application of an event related to the timer is delegated to the &lt;code&gt;Timer module. The &lt;/code&gt;apply_event` function returns the new timer state.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RoundTimeStarted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;timer:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RoundTimeTicked&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;InRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;timer:&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Timer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;apply_event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The game logic now deals with the timer: it can decide when the timer must be started, schedule the timer to be decremented a second later, and decrement and stop it.&lt;/p&gt;
&lt;p&gt;This is everything to know about changes in the game logic. We now need to talk about the delivery mechanism for the scheduled message.&lt;/p&gt;
&lt;h2 id=&quot;the-gameserver-as-scheduled-messages-delivery-mechanism&quot; tabindex=&quot;-1&quot;&gt;The &lt;code&gt;GameServer&lt;/code&gt; as scheduled messages delivery mechanism&lt;/h2&gt;
&lt;p&gt;The part title totally spoiled it: we’re going to use the &lt;code&gt;GameServer&lt;/code&gt; as our delivery mechanism for scheduled messages.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;Process.send_after&lt;/code&gt;, Elixir makes it very easy to send to a process a message in the future.&lt;/p&gt;
&lt;p&gt;Let’s add a private &lt;code&gt;schedule_messages&lt;/code&gt; function in the module:&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;schedule_messages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;scheduled_messages:&lt;/span&gt; scheduled_messages&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;delay&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; scheduled_messages&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;Process&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send_after&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:scheduled_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; delay&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  result&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function looks for the scheduled messages inside an &lt;code&gt;ActionResult&lt;/code&gt;, and for each one of them sends to &lt;code&gt;self()&lt;/code&gt;, the current &lt;code&gt;GameServer&lt;/code&gt; process,  a tuple message &lt;code&gt;{:scheduled_message, message}&lt;/code&gt; delayed by the amount of time requested by the game logic. It conveniently returns the &lt;code&gt;ActionResult&lt;/code&gt;, which means we can add this function to a chain of piped functions.&lt;/p&gt;
&lt;p&gt;Once the delay is over, the &lt;code&gt;GameServer&lt;/code&gt; process &lt;code&gt;handle_info&lt;/code&gt; callback is called with the message.&lt;/p&gt;
&lt;p&gt;We can create a clause for &lt;code&gt;handle_info&lt;/code&gt; and let it handle the message as a command and then store the state.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:scheduled_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  new_state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:noreply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; new_state&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ve already seen the &lt;code&gt;handle_command&lt;/code&gt; function. This is the function we call when the &lt;code&gt;GameServer receives a command&lt;/code&gt;. As a reminder, the function &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-game-server#handling-the-command&quot;&gt;dispatches the command to the game logic&lt;/a&gt;, &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-building-views-states-and-reacting-to-changes#modifying-the-state-while-the-game-is-running&quot;&gt;broadcast events for the view&lt;/a&gt;, and collect events to store them in the state.&lt;/p&gt;
&lt;p&gt;It’s the perfect place to add &lt;code&gt;schedule_message&lt;/code&gt;. Adding it here means that messages are scheduled when a command is handled and when a scheduled message is handled, precisely what we want. The first case takes care of starting the timer to tick when the round begins, and the second case deals with scheduling the next tick when a tick happens.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defp&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handle_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;command&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  command&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;schedule_messages&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;build_new_state&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in place, the game logic can express its need to be notified later to take decisions without being coupled to any infrastructure concerns. As promised, it required only a few code additions, no massive rework, and keeps the application well organized.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building an event-sourced game with Phoenix Liveview: Unit testing patterns</title>
      <link href="https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-unit-testing-patterns/"/>
      <updated>2021-04-07T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/phoenix-liveview-event-sourced-game-unit-testing-patterns/</id>
      <summary>This article is an overview of some useful patterns when testing event-sourced application.</summary>
      <content type="html">&lt;p&gt;We’ve seen a lot about the game but haven’t talked yet about testing. When dealing with event-sourced system testing comes with some interesting patterns. We’ll see some of them in this article.&lt;/p&gt;
&lt;h2 id=&quot;testing-in-event-sourced-system&quot; tabindex=&quot;-1&quot;&gt;Testing in event-sourced system&lt;/h2&gt;
&lt;p&gt;As with everything else with an event-sourced system, testing is based on events. The three tests parts, arrange, act and assert, can be mapped as follow:&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Arrange:&lt;/em&gt;  Create a history of events&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Act:&lt;/em&gt;  Recreate state from events and dispatch a command&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Assert:&lt;/em&gt;  Ensure the system produced some events&lt;/p&gt;
&lt;p&gt;Here is an example of a test ensuring that the second player is enlisted in the second team.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;WaitingRoomTest&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ExUnit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Case&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;async:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;br&gt;&lt;br&gt;  describe &lt;span class=&quot;token string&quot;&gt;&quot;Add player&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;    test &lt;span class=&quot;token string&quot;&gt;&quot;adds second players to the second team&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# Arrange: create a history where a player already joined the first team&lt;/span&gt;&lt;br&gt;	  history &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:player_1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;br&gt;      &lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# Act: Dispatch a command to add a second player based on the history&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# Rebuilding state from history is dealt with by GameState module&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;GameState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dispatch_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;AddPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player_name:&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:player_2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; history&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# Assert: Ensure that an event indicating that the second player joined the second team was produced&lt;/span&gt;&lt;br&gt;    assert &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;member?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:player_2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I like my tests to read easily and decided to introduce a new module to take advantage of Elixir’s piping to improve expressiveness. We can build on top of the fact that &lt;code&gt;GameState.dispatch_message&lt;/code&gt; returns an &lt;code&gt;ActionResult&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The first function in the new module checks if an event was published.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResultHelper&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;published_event?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;member?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; event&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can rewrite the previous test with this in place once we’ve imported the &lt;code&gt;ActionResultHelper&lt;/code&gt; module.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;test &lt;span class=&quot;token string&quot;&gt;&quot;adds second players to the second team&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  history &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:player_1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;br&gt;      &lt;br&gt;  assert &lt;span class=&quot;token function&quot;&gt;dispatch_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;AddPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player_name:&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:player_2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; history&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;published_events?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;name:&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:player_2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The separation between the act and the test’s assert parts is more blurry, but I think the test better conveys what the system does, so I’m ok with that.&lt;/p&gt;
&lt;h2 id=&quot;verify-that-an-error-is-returned&quot; tabindex=&quot;-1&quot;&gt;Verify that an error is returned&lt;/h2&gt;
&lt;p&gt;We can add more functions to the &lt;code&gt;ActionResultHelper&lt;/code&gt; module to verify that the game’s behavior is as expected.  One case is ensuring that the game correctly returns an error when needed.&lt;/p&gt;
&lt;p&gt;First, let’s add a function.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResultHelper&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;errored_with?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;error:&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; expected_error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  error &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; expected_error&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, in the tests, we can write something like&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;test &lt;span class=&quot;token string&quot;&gt;&quot;refuses a player with a name already taken&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  player_name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:same_player_name&lt;/span&gt;&lt;br&gt;&lt;br&gt;  history &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;name:&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;  assert &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;AddPlayer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player_name:&lt;/span&gt; player_name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;         &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;history&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;         &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;errored_with?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:player_name_not_available&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Again, this reads very well!&lt;/p&gt;
&lt;h2 id=&quot;ensuring-that-a-message-is-scheduled&quot; tabindex=&quot;-1&quot;&gt;Ensuring that a message is scheduled&lt;/h2&gt;
&lt;p&gt;We’ve seen that the application sometimes needs to &lt;a href=&quot;/articles/phoenix-liveview-event-sourced-game-building-decrementing-the-timer/&quot;&gt;schedule a message that it would like to receive in the future&lt;/a&gt;. We can follow the same pattern to verify that the game logic well produces these messages.&lt;/p&gt;
&lt;p&gt;Introduce a new function to the helper module&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResultHelper&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scheduled_message_in?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;scheduled_messages:&lt;/span&gt; scheduled_messages&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; time&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;member?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;scheduled_messages&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;time&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And write a test. Here we ensure that when a &lt;code&gt;Tick&lt;/code&gt; message is dispatched the next one is scheduled for one second later.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;test &lt;span class=&quot;token string&quot;&gt;&quot;schedules a tick message for 1 second later&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;br&gt;  history &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;br&gt;&lt;br&gt;  assert &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Tick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;         &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;history&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;         &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;scheduled_message_in?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Tick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token atom symbol&quot;&gt;:timer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;being-lazy&quot; tabindex=&quot;-1&quot;&gt;Being lazy&lt;/h2&gt;
&lt;p&gt;Sometimes a command creates a lot of events, and it is painful to manually build them all to create a history of a game after several rounds.&lt;br&gt;
We can streamline the arrange part of tests if we use commands to create the history. In that case, we need to collect the events produced when the command is dispatched and add them to the current list of events to complete the history.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;defmodule&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResultHelper&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;start_history&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_and_collect_events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; history&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;      history&lt;br&gt;      &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dispatch_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; history &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;do:&lt;/span&gt; events&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here I’ve added a few more functions to the helper module. &lt;code&gt;StartHistory&lt;/code&gt; returns an empty &lt;code&gt;%ActionResult{}&lt;/code&gt; structure. The &lt;code&gt;dispatch_and_collect_events&lt;/code&gt; function dispatches a message to the game using the events stored in an &lt;code&gt;ActionResult&lt;/code&gt; and adds events resulting from the dispatch to it. &lt;code&gt;Events&lt;/code&gt; function returns the events contained in the &lt;code&gt;ActionResult&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We can use these functions to build history. For instance, in the next snippet, we build the history up to after the game starts.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;history &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;start_history&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;#ActionResult.add&lt;/span&gt;&lt;br&gt;   &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:player_A1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;   &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:player_A2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;   &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:player_B1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;   &lt;span class=&quot;token module class-name&quot;&gt;PlayerJoinedTeam&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;player:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;player&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token atom symbol&quot;&gt;:player_B2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;team:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_and_collect_events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;StartGame&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;blue_deck:&lt;/span&gt; blue_cards&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;red_deck:&lt;/span&gt; red_cards&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;dictionary:&lt;/span&gt; words&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another instance where it’s super helpful is for the last tick of round, where many things are going on.&lt;/p&gt;
&lt;p&gt;With the three following lines, we’re now able to start a round, fast-forward to the penultimate tick and trigger one last tick.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_and_collect_events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;StartRound&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;RoundTimeTicked&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;remaining_time:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_and_collect_events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;Tick&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;being-super-lazy&quot; tabindex=&quot;-1&quot;&gt;Being super lazy&lt;/h2&gt;
&lt;p&gt;When a player marks a word as guessed, the game produces a lot of events. We’ve seen how the introduction of the &lt;code&gt;dispatch_and_collect_events&lt;/code&gt; can help here. One last issue persists when testing that the game correctly ends after 20 words guesses; we need to copy-paste &lt;code&gt;dispatch_and_collect_events(%MarkWordAsGuessed{})&lt;/code&gt; a lot.&lt;/p&gt;
&lt;p&gt;We can fix this by adding an extra, optional parameter to &lt;code&gt;dispatch_and_collect_events&lt;/code&gt;, which states how many times we want to repeat the message.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_and_collect_events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; history&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opts &lt;span class=&quot;token operator&quot;&gt;&#92;&#92;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  repeat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;Keyword&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;opts&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token atom symbol&quot;&gt;:repeat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token module class-name&quot;&gt;Enum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;..&lt;/span&gt;repeat&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; history&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; _x&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token attr-name&quot;&gt;events:&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    result&lt;br&gt;    &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token module class-name&quot;&gt;ActionResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;GameState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dispatch_message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; events&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opts&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;end&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This change allows reducing the amount of typing necessary to build the history.&lt;/p&gt;
&lt;p&gt;Here we build a history where a player marked 20 words as guessed.&lt;/p&gt;
&lt;pre class=&quot;language-elixir&quot;&gt;&lt;code class=&quot;language-elixir&quot;&gt;history&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dispatch_and_collect_events&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token module class-name&quot;&gt;MarkWordAsGuessed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;repeat:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That the last one of the helpful patterns for testing applications built using event-sourcing I’ve introduced in this project. I hope they can give you some ideas for your own projects!&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>An alias to learn aliases</title>
      <link href="https://blog.charlesdesneuf.com/articles/an-alias-to-learn-aliases/"/>
      <updated>2021-06-11T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/an-alias-to-learn-aliases/</id>
      <summary>I’m trying to learn aliases to reduce the typing I need in the console but I can’t remember them. I’ve created an alias to help me !</summary>
      <content type="html">&lt;p&gt;I’m trying to save some time by reducing the number of keystrokes I need to do something.&lt;/p&gt;
&lt;p&gt;I’m pretty efficient with my IDE’s shortcuts, but I have vast room for improvement in my console.&lt;/p&gt;
&lt;p&gt;The obvious solution there is relying on aliases.&lt;/p&gt;
&lt;p&gt;I’ve been using the git ssh plugin for several years, but I only use a handful of the provided aliases, mostly because I never took the time to memorize past the few main ones.&lt;/p&gt;
&lt;p&gt;Now that I’m trying to save some typing, I’m learning the other ones. I have an issue, though: I spend a lot of time looking at the documentation to know which alias shortens the command I want to enter. In that case, it means I switch to my browser, get the right tab, or go to the website, use the search to find the suitable alias by typing part of the command. I don’t save a lot of keystrokes, and I’m surely not gaining time.&lt;/p&gt;
&lt;p&gt;Please don’t take me wrong; time spend learning or practicing to improve in the long run is time well spent. Nevertheless, I decided to find a way to enhance my learning process.&lt;/p&gt;
&lt;p&gt;The best way to reduce the amount of time needed to find the proper alias for a command is to stay in the console and avoid looking at the documentation.&lt;/p&gt;
&lt;p&gt;After a quick search, I discovered that the &lt;code&gt;alias&lt;/code&gt; command lists all aliases set up. That’s a start.&lt;/p&gt;
&lt;p&gt;Combining &lt;code&gt;alias&lt;/code&gt; and &lt;code&gt;grep&lt;/code&gt; makes it efficient to find the correct alias for the job.&lt;/p&gt;
&lt;p&gt;That still requires some typing, if your shell doesn’t have some sort if autocomplete at least.&lt;/p&gt;
&lt;p&gt;As I’m in a quest for laziness and poking around with aliases, I decided to add a new one, &lt;code&gt;fa&lt;/code&gt;, for “find alias” defined as &lt;code&gt;alias fa=&#39;alias | grep&#39;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./fa_run_stash.png&quot; alt=&quot;An example: searching for all aliases about stash&quot;&gt;&lt;/p&gt;
&lt;p&gt;Just two letters, no more windows switching, and a lot of time saved.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Generating dates and UUIDs easily in PhpStorm</title>
      <link href="https://blog.charlesdesneuf.com/articles/generating-dates-and-uuids-easily-in-phpstorm/"/>
      <updated>2021-08-13T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/generating-dates-and-uuids-easily-in-phpstorm/</id>
      <summary>Typing a date or generating an UUID is something you have to do a lot while writing tests. Here is a tip that allows to do so with only a few keystrokes in PhpStorm.</summary>
      <content type="html">&lt;p&gt;Legacy codebases tend to lack tests. One way to introduce some safety net before messing around with the code is to add characterization tests.&lt;br&gt;
In this kind of test, we feed some input to the system under test and look at the outputs, ensuring that they stay the same test run after test run. It’s far easier to fix inputs once and for all.&lt;/p&gt;
&lt;p&gt;When creating this type of test, we want to avoid moving or random values. Two of the most common are dates and UUIDs. While it’s possible to generate UUIDs using external tools, there is room for improvement if you want to do everything directly in PhpStorm. Even if typing a date is not as painful, we can gain some time here too.&lt;/p&gt;
&lt;p&gt;PhpStorm, and probably all IntelliJ IDEs, allow creating Live templates. Live templates are templates that we can expand by typing an abbreviation and triggering the autocomplete. What’s even more remarkable is that they can evaluate &lt;a href=&quot;https://www.jetbrains.com/help/phpstorm/template-variables.html#predefined_functions&quot;&gt;expressions&lt;/a&gt; when the abbreviation is expanded. This is what we’ll use.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./social.gif&quot; alt=&quot;Generating dates and UUIDs with a only few keystrokes.&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;generating-the-current-date&quot; tabindex=&quot;-1&quot;&gt;Generating the current date&lt;/h2&gt;
&lt;p&gt;For generating the current date, we can use the &lt;code&gt;date&lt;/code&gt; expression provided by PhpStorm and bind it to a variable.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In PhpStorm settings, go to &lt;code&gt;Editor &amp;gt; Live Templates&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Expand the PHP section&lt;/li&gt;
&lt;li&gt;Click on the Add button, &lt;code&gt;+&lt;/code&gt; on the right side, and select &lt;code&gt;Live template&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In the abbreviation field, enter &lt;code&gt;$date$&lt;/code&gt;. This is what we’ll type to expand the template.&lt;/li&gt;
&lt;li&gt;In the template text field, enter &lt;code&gt;&#39;$date$&#39;$END$&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Edit variables&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In the line for the variable named &lt;code&gt;date&lt;/code&gt;, add the expression &lt;code&gt;date(&amp;quot;yyyy-MM-dd’T&#39;HH:mm:ss.SSSz&amp;quot;)&lt;/code&gt;  and select &lt;code&gt;Skip if defined&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Define&lt;/code&gt; below the &lt;code&gt; ⚠️ No applicable contexts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;PHP &amp;gt; Expression&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Save and close the settings with the &lt;code&gt;Ok&lt;/code&gt; button.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;generating-a-random-uuid&quot; tabindex=&quot;-1&quot;&gt;Generating a random UUID&lt;/h2&gt;
&lt;p&gt;Generating a UUID is slightly different because PhpStorm doesn’t provide a function to create UUIDs directly, but it, fortunately, allows to run Groovy scripts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In PhpStorm settings, go to &lt;code&gt;Editor &amp;gt; Live Templates&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Expand the PHP section&lt;/li&gt;
&lt;li&gt;Click on the Add button, &lt;code&gt;+&lt;/code&gt; on the right side, and select &lt;code&gt;Live template&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In the abbreviation field, enter &lt;code&gt;$uuid$&lt;/code&gt;. This is what we’ll type to expand the template.&lt;/li&gt;
&lt;li&gt;In the template text field, enter &lt;code&gt;&#39;$uuid$&#39;$END$&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Edit variables&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;In the line for the variable named &lt;code&gt;uuid&lt;/code&gt;, add the expression &lt;code&gt;groovyScript(&amp;quot;UUID.randomUUID().toString()&amp;quot;)&lt;/code&gt; and select &lt;code&gt;Skip if defined&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;Define&lt;/code&gt; below the &lt;code&gt; ⚠️ No applicable contexts&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;PHP &amp;gt; Expression&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Save and close the settings with the &lt;code&gt;Ok&lt;/code&gt; button.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And voila, we now can quickly insert the current date or a UUID in our code.&lt;/p&gt;
&lt;p&gt;Thanks to PhpStorm allowing us to execute Groovy code, we can probably think of other ideas and save more time. For instance, it would probably make sense to generate a random date instead of the current one, as we could be in a particular case if the code has conditions based on the current date.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>An alias to easily debug PHP scripts</title>
      <link href="https://blog.charlesdesneuf.com/articles/an-alias-to-easily-debug-php-scripts/"/>
      <updated>2021-12-28T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/an-alias-to-easily-debug-php-scripts/</id>
      <summary>Simplify PHP debugging with a custom shell alias that handles all XDebug configuration options. Save time, keystrokes, and mental overhead by wrapping complex debugging commands in an easy-to-remember alias.</summary>
      <content type="html">&lt;p&gt;As I said before, I’m trying to save some time when using the CLI, and I rely more and more on aliases.&lt;/p&gt;
&lt;p&gt;Aliases are fantastic to save some keystrokes, and they also significantly help with something else: freeing mental space.&lt;/p&gt;
&lt;p&gt;Some commands take a lot of arguments and options, and it’s not always simple to remember all of them. I’ve decided to stop remembering everything and created an abstraction on top of the one I often use, hiding the long list of arguments under an alias or a command. If I still have trouble remembering the alias, I can still rely on my &lt;a href=&quot;/articles/an-alias-to-learn-aliases/&quot;&gt;alias to find aliases&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;One of the examples of such a command with a long list of arguments is the PHP command when it comes to debugging a PHP script.&lt;/p&gt;
&lt;p&gt;On my machine, with PhpStorm, it goes like &lt;code&gt;php -dxdebug.mode=debug -dxdebug.client_host=127.0.0.1 -dxdebug.client_port=9000 -dxdebug.start_with_request=yes script.php&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You probably could have different options on your machine.&lt;/p&gt;
&lt;p&gt;I’ve introduced a simple alias, &lt;code&gt;xdebug&lt;/code&gt;, to avoid thinking about specifing mode, port and client:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;xdebug&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;php -dxdebug.mode=debug &#92;&lt;br&gt; -dxdebug.client_host=127.0.0.1&lt;br&gt; &#92; -dxdebug.client_port=9000&lt;br&gt; &#92; -dxdebug.start_with_request=yes&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, debugging a script is as easy as &lt;code&gt;xdebug script.php&lt;/code&gt; and I don’t have to look for the documentation, or look through my zsh history, to see how I need to construct my command.&lt;/p&gt;
&lt;p&gt;Keystrokes and time saved, memory freed!&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Composing data using Postgres Foreign Data Wrapper</title>
      <link href="https://blog.charlesdesneuf.com/articles/composing-data-using-postgres-foreign-data-wrapper/"/>
      <updated>2022-02-18T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/composing-data-using-postgres-foreign-data-wrapper/</id>
      <summary>Composing data from several databases can be tricky, even worse if we need to do some paging, filtering and sorting. PostgreSQL comes with an extension making our life easier by creating remote table: tables based on a table from another server.</summary>
      <content type="html">&lt;p&gt;Say you decided to go the micro-service way, split your database into pieces, and are now requested a feature that needs to display data stored from several of these databases. Fetching the data from several sources and composing it somehow is a totally valid solution. Unfortunately, it starts to be tricky if you need to do some paging, sorting, and filtering based on the data from several of the databases. A solution to this problem could be to denormalize data, create some data store just for that purpose and copy the relevant data there.&lt;/p&gt;
&lt;p&gt;A more accessible alternative solution is available if you’re using PostgreSQL: thanks to the Foreign Data Wrapper extension, one Postgres database can query tables from another database as if they were on the same serve.&lt;/p&gt;
&lt;h2 id=&quot;displaying-race-results-for-my-pony-club&quot; tabindex=&quot;-1&quot;&gt;Displaying race results for my pony club&lt;/h2&gt;
&lt;p&gt;Let’s imagine that I’m currently contracting for a Pony Club. They’re really into IT and decided to build a system with two microservices. The first one knows about the club’s ponies, the second one stores their results during some competitions. It would be so nice to display our ponies&#39; results alongside some information about them, and why not do some filtering.&lt;/p&gt;
&lt;p&gt;First let’s create our two service databases.&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;version: &#39;3.5&#39;&lt;br&gt;&lt;br&gt;services:&lt;br&gt;  postgres_ponies:&lt;br&gt;    image: postgres&lt;br&gt;    container_name: ponies_db&lt;br&gt;    environment:&lt;br&gt;      POSTGRES_USER: postgres&lt;br&gt;      POSTGRES_PASSWORD: postgres&lt;br&gt;    ports:&lt;br&gt;      - &quot;5432:5432&quot;&lt;br&gt;&lt;br&gt;  postgres_race_results:&lt;br&gt;    image: postgres&lt;br&gt;    container_name: race_results_db&lt;br&gt;    environment:&lt;br&gt;      POSTGRES_USER: postgres&lt;br&gt;      POSTGRES_PASSWORD: postgres&lt;br&gt;    ports:&lt;br&gt;      - &quot;5433:5432&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This docker compose file will start two Postgres databases, one named &lt;code&gt;ponies_db&lt;/code&gt; and the other one &lt;code&gt;race_results_db&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We will then create some tables and add data to the two instances.&lt;/p&gt;
&lt;p&gt;Once connected to the &lt;code&gt;ponies_db&lt;/code&gt;, with &lt;code&gt;docker exec -ti ponies_db psql -U postgres&lt;/code&gt; run the two following queries:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;EXISTS&lt;/span&gt; ponies &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;   id &lt;span class=&quot;token keyword&quot;&gt;serial&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;   name &lt;span class=&quot;token keyword&quot;&gt;VARCHAR&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;UNIQUE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;   &lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DEFAULT&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INTO&lt;/span&gt; ponies&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;VALUES&lt;/span&gt;&lt;br&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Noisette&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Griotte&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Eole&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we created a &lt;code&gt;ponies&lt;/code&gt; table and inserted three ponies, with an id, a name, and some status stored as an integer.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;race_results_db&lt;/code&gt;, that you can connect to with &lt;code&gt;docker exec -ti race_results psql -U postgres&lt;/code&gt;, run:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;EXISTS&lt;/span&gt; race_results &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;   id &lt;span class=&quot;token keyword&quot;&gt;serial&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;KEY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;   race &lt;span class=&quot;token keyword&quot;&gt;VARCHAR&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;   pony &lt;span class=&quot;token keyword&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;   timing &lt;span class=&quot;token keyword&quot;&gt;INT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INTO&lt;/span&gt; race_results&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;race&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pony&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; timing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;VALUES&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Fun fun&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;137&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Fun fun&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;125&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Fun fun&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;133&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Epic party race&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;79&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Epic party race&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;77&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This just created a &lt;code&gt;race_results&lt;/code&gt; table, each row being an id, a race name as a string, a pony - an integer, mapping to the id of a pony in the &lt;code&gt;ponies_db&lt;/code&gt; database, and some timing in the second.&lt;/p&gt;
&lt;p&gt;Now, let’s create the link between the two databases.&lt;/p&gt;
&lt;p&gt;Still in the &lt;code&gt;race_results&lt;/code&gt; database install the &lt;code&gt;postgres_fdw&lt;/code&gt; extension:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; EXTENSION &lt;span class=&quot;token keyword&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;EXISTS&lt;/span&gt; postgres_fdw&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Declare a remote server, pointing to the &lt;code&gt;ponies_db&lt;/code&gt; server:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; SERVER ponies&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DATA&lt;/span&gt; WRAPPER postgres_fdw&lt;br&gt;OPTIONS &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;host &lt;span class=&quot;token string&quot;&gt;&#39;ponies_db&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; port &lt;span class=&quot;token string&quot;&gt;&#39;5432&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and tell which user to use for the connection:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;USER&lt;/span&gt; MAPPING &lt;span class=&quot;token keyword&quot;&gt;FOR&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;USER&lt;/span&gt;&lt;br&gt;SERVER ponies&lt;br&gt;OPTIONS &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;postgres&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; password &lt;span class=&quot;token string&quot;&gt;&#39;postgres&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At this point, we can create a remote table, mapping from the &lt;code&gt;ponies’table from the &lt;/code&gt;ponies_db` database:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; ponies &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;  id &lt;span class=&quot;token keyword&quot;&gt;serial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  name &lt;span class=&quot;token keyword&quot;&gt;VARCHAR&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;SERVER ponies&lt;br&gt;OPTIONS &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;schema_name &lt;span class=&quot;token string&quot;&gt;&#39;public&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; table_name &lt;span class=&quot;token string&quot;&gt;&#39;ponies&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now we can run a query with a join between race results and ponies, using the newly created foreign table:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;race&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timing&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; race_results r&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; ponies p &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pony &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1,Noisette,Fun fun,137,1&lt;br&gt;2,Griotte,Epic party race,79,2&lt;br&gt;2,Griotte,Fun fun,125,2&lt;br&gt;3,Eole,Epic party race,77,0&lt;br&gt;3,Eole,Fun fun,133,0&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;but%2C-but%2C-but...-the-coupling&quot; tabindex=&quot;-1&quot;&gt;But, but, but... the coupling&lt;/h2&gt;
&lt;p&gt;You might be mumbling something about how nice that’s solution is, but it creates some coupling with the schema of the &lt;code&gt;ponies_db&lt;/code&gt;, and you’d be right. If the team working around the &lt;code&gt;ponies_db&lt;/code&gt; feels the need to rename a column or change how they handle their statuses, it will break everything.&lt;/p&gt;
&lt;p&gt;Fortunately, we can create foreign tables based on views. This is really nice as it means the &lt;code&gt;ponies_db&lt;/code&gt; team can have an abstraction layer to hide their schema behind. If at some point they decide to change a part of the schema, as long as they modify the view to ensure not to break the contract, everything will keep running.&lt;/p&gt;
&lt;p&gt;Furthermore, having a view helps expose a better API. So far, we don’t know what the integer pony statuses mean. In the new view, we can map each value to a string, increasing the understandability of the API.&lt;/p&gt;
&lt;p&gt;Let’s make the changes.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;ponies_db&lt;/code&gt;, we create a &lt;code&gt;public_ponies&lt;/code&gt; view based on the &lt;code&gt;ponies&lt;/code&gt; table:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;VIEW&lt;/span&gt; public_ponies &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt;&lt;br&gt;           id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;           name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token keyword&quot;&gt;CASE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt;&lt;br&gt;               &lt;span class=&quot;token keyword&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;resting&#39;&lt;/span&gt;&lt;br&gt;               &lt;span class=&quot;token keyword&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;available&#39;&lt;/span&gt;&lt;br&gt;               &lt;span class=&quot;token keyword&quot;&gt;WHEN&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;THEN&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;injured&#39;&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token keyword&quot;&gt;END&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;AS&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; ponies&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the &lt;code&gt;race_results_db&lt;/code&gt; we will remove the existing remote table and create a new one based on the view:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;DROP&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; ponies&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; ponies &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;    id &lt;span class=&quot;token keyword&quot;&gt;serial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    name &lt;span class=&quot;token keyword&quot;&gt;VARCHAR&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;VARCHAR&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;SERVER ponies&lt;br&gt;OPTIONS &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;schema_name &lt;span class=&quot;token string&quot;&gt;&#39;public&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; table_name &lt;span class=&quot;token string&quot;&gt;&#39;public_ponies&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can rerun our query with the join:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;race&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;timing&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;status&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; race_results r&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;JOIN&lt;/span&gt; ponies p &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; r&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pony &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; p&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and we will get&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1,Noisette,Fun fun,137,available&lt;br&gt;2,Griotte,Epic party race,79,injured&lt;br&gt;2,Griotte,Fun fun,125,injured&lt;br&gt;3,Eole,Epic party race,77,resting&lt;br&gt;3,Eole,Fun fun,133,resting&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;limitations&quot; tabindex=&quot;-1&quot;&gt;Limitations&lt;/h2&gt;
&lt;p&gt;While this solution allows doing powerful things without much work, it’s not magical. The two databases need to talk to each other and exchange data which means that if they are far away from one the other, the query will take more time than if the data was all on the same server. Depending on your use case, it might be an issue or not. In the case of an asynchronous job, it’s probably not; for a synchronous query, you’ll have to see for yourself. Nevertheless, it would still be a decent solution to have while you’re creating a read database aggregating all the data.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Using Property Based Testing to test priorities</title>
      <link href="https://blog.charlesdesneuf.com/articles/using-property-based-testing-to-test-priorities/"/>
      <updated>2022-02-28T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/using-property-based-testing-to-test-priorities/</id>
      <summary>Learn how to use Property Based Testing to validate message priorities in complex conditional systems. Transform a difficult-to-test switch statement into testable components using the Composite pattern and Fast-Check framework.</summary>
      <content type="html">&lt;p&gt;I was recently working on a project with an Angular frontend where we displayed an action button. That button had to be disabled when some conditions were met.&lt;/p&gt;
&lt;p&gt;Examples of conditions are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;not the proper access level&lt;/li&gt;
&lt;li&gt;not enough &amp;quot;points in the wallet&amp;quot; to do the action&lt;/li&gt;
&lt;li&gt;action can’t be performed on the selected item&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is no complex logic to knowing if a condition is triggered. Nevertheless, the service tasked with deciding if a condition was met was growing and was taking on more dependencies.&lt;/p&gt;
&lt;p&gt;When the application was in a state where a condition was on, we displayed a tooltip to explain why the button was disabled.&lt;/p&gt;
&lt;p&gt;Now, this is where the fun starts. Multiple conditions could be detected at a point in time: the action can’t be performed on the selected item, and you don’t even have enough points anyway, for instance.&lt;/p&gt;
&lt;p&gt;We could argue that it would be better to display all messages at once to avoid our users playing the annoying error message game where they fix an error just to discover a new one again and again. Unfortunately for the user, it was decided to display error messages one by one for some reason.&lt;/p&gt;
&lt;p&gt;It was unfortunate for us as well, as we now had to decide which message to display when several conditions were met. Obviously, not all states are born equals, and some are more important than others. The logic in the service was now also tasked with ordering the messages.&lt;/p&gt;
&lt;p&gt;The code looked something like this:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuardLogic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;vote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;authenticationService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isAuthenticated&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;You need to be authenticated&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;anotherService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;someState &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;not_a_good_state&#39;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;You’re not in a good state&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedItem&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;totalPointsNeeded &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;walletService&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;availablePoints&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;You don’t have enough points in your wallet&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;selectedItem&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isSomething&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;You can’t do this on an item that is in isSomething state&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I changed it slightly to remove everything related to Observables as it’s not the interesting part and to hide some of the business logic. You can still get the idea.&lt;/p&gt;
&lt;p&gt;I was pairing with a colleague, planning to add a new message based on a condition that would require taking on a new dependency, and with almost no tests to help us.&lt;/p&gt;
&lt;p&gt;As you can imagine, if you want to cover all cases and ensure that the order of messages keeps being respected when multiple conditions are on, you need a lot of tests. If you’re only interested in if the condition is met or not, and not on the data required by each condition to decide if it’s on, we’re talking 2^(number of conditions) test cases. For the code above, 16 test cases are needed. With the new condition we were about to add, it grows to 32.&lt;/p&gt;
&lt;p&gt;We wanted to improve the code and agreed that it would be better to have each condition on its own, where we could test it separately and have a sort of glue class, selecting the right message to display, also tested aside.&lt;/p&gt;
&lt;p&gt;As we weren’t great Angular devs and were lost with the Observables and the way to test them, we didn’t refactor the code and conformed to the current design. It bothered me, and I tried to see how I could improve it and better split testing conditions and message display.&lt;/p&gt;
&lt;p&gt;I’ll pretend we had only three conditions to simplify the examples for the following.&lt;/p&gt;
&lt;p&gt;First, let’s extract the conditions:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ErrorMessage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PositiveMessage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GuardMessage&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; ErrorMessage &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; PositiveMessage&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuard&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;vote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessage&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuardA&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuard&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;vote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessage &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Error A&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuardB&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuard&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;vote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessage &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Error B&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuardC&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuard&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;vote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessage &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Error C&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we defined a &lt;code&gt;ButtonGuard&lt;/code&gt; type, containing a &lt;code&gt;vote&lt;/code&gt; method that can reply with an &lt;code&gt;ErrorMessage&lt;/code&gt; or a &lt;code&gt;PositiveMessage&lt;/code&gt;. Each guard, or condition, implements this type, and can get the needed dependencies to decide if it wants to return a message or not. As all guards are separated, they can easily be tested.&lt;/p&gt;
&lt;p&gt;Next, we add another type of &lt;code&gt;ButtonGuard&lt;/code&gt;, which takes all the other guards, requests their votes, and returns the first &lt;code&gt;ErrorMessage&lt;/code&gt; found. As with the switch case in the first design, the display order is still represented: here, it’s the order of the guards we placed in the array.&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuardLogic&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; guardA&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ButtonGuardA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; guardB&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ButtonGuardB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; guardC&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ButtonGuardC&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;vote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;guardA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;guardB&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;guardC&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guard&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; guard&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;vote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guardMessage &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; guardMessage &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If no &lt;code&gt;ErrorMessage&lt;/code&gt; was returned the &lt;code&gt;ButtonGuardLogic&lt;/code&gt; returns with an empty string, the &lt;code&gt;PositiveMessage&lt;/code&gt; type.&lt;/p&gt;
&lt;p&gt;You may have noticed that this is the Composite design pattern in action.&lt;/p&gt;
&lt;p&gt;Arguably, this code is already an improvement. We still need to test that the messages are displayed in the expected order, and it’s time for Property Based Testing to come into play.&lt;/p&gt;
&lt;p&gt;PBT frameworks work by trying many combinations of more or less random data on each test run for properties we’ve defined. The canonical example of a property is reversing twice a string should return that same string. A PBT framework will give us a lot of strings to try that property, including some we probably wouldn’t think about trying (empty string, &amp;quot;null&amp;quot;, string with non-Latin characters, a string containing numbers, ...).&lt;/p&gt;
&lt;p&gt;Here the property we want to test is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;The displayed error message is the message with the highest priority returned from all guards.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Because we have extracted all conditions in simple guards, we can now easily say which of them are in an error state or not by providing a stubbed implementation. The PBT framework’s job is to tell us which guards are in an error state. It’s equivalent to saying that the PBT framework has to generate a list of boolean values, one for each guard, and we will map these booleans to set up the stubbed implementations.&lt;/p&gt;
&lt;p&gt;As we know which guards are in an error state, we also know which error messages are produced and can get the one with the highest priority out of them. Then we can compare that value with the one returned by the implementation.&lt;/p&gt;
&lt;p&gt;We need to have a way to control our individual guards. For this, I introduced a &lt;code&gt;FakeButtonGuard&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FakeButtonGuard&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuard&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    voteValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;vote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessage &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;voteValue&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will also need some constants that will come in handy later:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; errorMessageByGuard &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Error message from guard A&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token constant&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Error message from guard B&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token constant&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;Error message from guard C&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; positiveMessage &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; guards&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;guardName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; FakeButtonGuard&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token constant&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FakeButtonGuard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token constant&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FakeButtonGuard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token constant&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FakeButtonGuard&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; priorities &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;priority&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Error message from guard A&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Error message from guard B&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Error message from guard C&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; guardCount &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;keys&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guards&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For each guard, we define its error message. Here it’s a string, and it could be a constant defined in the guard classes as an improvement.&lt;br&gt;
We also instantiate fake button guards for each guard and list error messages in the expected priority order. &lt;code&gt;guardCount&lt;/code&gt; gives us a simple way to get the number of guards.&lt;/p&gt;
&lt;p&gt;Before each test, we instantiate a &lt;code&gt;ButtonGuardLogic&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; buttonGuardLogic&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ButtonGuardLogic&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;beforeEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    buttonGuardLogic &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ButtonGuardLogic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;        guards&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;A&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        guards&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;B&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        guards&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;C&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We are ready to add our test.&lt;/p&gt;
&lt;p&gt;Using &lt;a href=&quot;https://github.com/dubzzz/fast-check&quot;&gt;Fast-Check&lt;/a&gt;, a PBT framework for typescript, we generate an array of three, the number of guards, boolean values.&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;displays error message by order of priority.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    fc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;        fc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;minLength&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; guardCount&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxLength&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; guardCount&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activations&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;           &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to define a few functions.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;buildGuardValues&lt;/code&gt; function associates the activations of guards with their messages. It produces a dictionary where each guard name (A, B, or C) maps to either an empty string when the boolean value is true or the error message associated with the guard otherwise.&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; buildGuardValues &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activations&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessagesByGuardName &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;br&gt;  Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;errorMessageByGuard&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;reduce&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guardValues&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;guardName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; errorMessage&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; index&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        guardValues&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;guardName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; activations&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; positiveMessage &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; errorMessage&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; guardValues&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;setupGuards&lt;/code&gt; function sets each of the &lt;code&gt;FakeGuards&lt;/code&gt; with their expected responses:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;setupGuards&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guardValues&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessagesByGuardName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;br&gt;  Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guardValues&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;guardName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; guards&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;guardName&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;voteValue &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With these two functions, we can map the generated array of boolean to the list of the guards&#39; messages and initialize each guard with the value it’s expected to return.&lt;/p&gt;
&lt;p&gt;Next, we need to go through all the expected messages to find the one with the highest priority. To do this, I introduced the three following functions:&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; getPriority &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guardMessage&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;&lt;br&gt;  Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;entries&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;priorities&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;_priority&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; errorMessage&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; errorMessage &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; guardMessage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;priority&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _errorMessage&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseInt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;priority&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shift&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; compareGuardMessagePriority &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guardMessage1&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessage&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; guardMessage2&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessage&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CompareResult &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;br&gt;  &lt;span class=&quot;token function&quot;&gt;getPriority&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guardMessage1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getPriority&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guardMessage2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; getMessageWithHighestPriority &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guardMessages&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessage&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; GuardMessage &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;br&gt;    guardMessages&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sort&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;compareGuardMessagePriority&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shift&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;getPriority&lt;/code&gt; gets the priority associated with an error message.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;compareGuardMessagePriority&lt;/code&gt; compares two messages.&lt;/p&gt;
&lt;p&gt;Finally, &lt;code&gt;getMessageWithHighestPriority&lt;/code&gt; gives the message with the highest priority using the &lt;code&gt;compareGuardMessagePriority&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;The message returned by &lt;code&gt;getMessageWithHighestPriority&lt;/code&gt; is the one we expect given the provided array of booleans.&lt;/p&gt;
&lt;p&gt;We can now tie all the pieces together to write the body of the test.&lt;/p&gt;
&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;displays error message by order of priority.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    fc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;property&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;        fc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;minLength&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; guardCount&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; maxLength&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; guardCount&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activations&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; guardValues &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;buildGuardValues&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activations&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token function&quot;&gt;setupGuards&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guardValues&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; buttonActivationWithHighestPriority &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getMessageWithHighestPriority&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Object&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;values&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;guardValues&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; context &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;buildContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;activations&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;            &lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buttonGuardLogic&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;vote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;to&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;equal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;buttonActivationWithHighestPriority&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We now have a test that will cover a lot of permutations of error activations.&lt;/p&gt;
&lt;p&gt;When it is time to add a new condition, it will easily be created in its own &lt;code&gt;ButtonGuard&lt;/code&gt;; that guard will easily be testable independently of the others. Then we will have to slightly modify the &lt;code&gt;ButtonGuardLogic&lt;/code&gt; to add the new guard and its error message and let Fast-Check deal with testing the new permutations.&lt;/p&gt;
&lt;p&gt;That&#39;s it !&lt;/p&gt;
&lt;p&gt;And if you need some help with your test I think I can help. Have a look at &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;my video course&lt;/a&gt; in French or &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let&#39;s have a chat&lt;/a&gt; and see what we can do together.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Test suite guidelines</title>
      <link href="https://blog.charlesdesneuf.com/articles/test-suite-guidelines/"/>
      <updated>2022-03-09T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/test-suite-guidelines/</id>
      <summary>When looking at a test harness, it’s sometimes hard to decide which type of test to write, what it should cover, how we should write it. I believe we would stop losing time and remove frustration by adding guidelines for our test suites.</summary>
      <content type="html">&lt;p&gt;Yesterday I watched again the &lt;a href=&quot;https://burritalks.io/talks/justin-searls-breaking-up-with-your-test-suite/?utm_medium=referral&amp;amp;utm_source=charlesdesneuf.com&amp;amp;utm_campaign=choripam_test_suite_guideline_article&quot;&gt;&amp;quot;Breaking up (with) your test suite&amp;quot; talk&lt;/a&gt; by &lt;a href=&quot;https://twitter.com/searls&quot;&gt;Justin Searls&lt;/a&gt;, and it resonated with an idea I recently had while working on a project for one of my clients. I even suspect that my idea is actually Justin’s one and that my brain conveniently remembered it when I needed it.&lt;/p&gt;
&lt;p&gt;I planned to write about this for a while, and I guess it’s a sign that I should stop postponing.&lt;/p&gt;
&lt;h2 id=&quot;backstory&quot; tabindex=&quot;-1&quot;&gt;Backstory&lt;/h2&gt;
&lt;p&gt;I worked with a colleague on the Angular front-end part of a project, adding a feature, and wanted to write some tests. I have been working on back-end stuff for some time now, and I’m not used to writing tests for components. We went around with my pair partner, looking at all the test files available, and they all looked quite similar and different at the same time. Some tests were exercising the kind of code we were about to implement but in a way that looked strange to us. It wasn’t convincing, but we couldn’t decide ourselves to write the tests differently. After all, we were just two back-end devs on a front-end codebase new to us. Who were we to change the way to test that code? We probably didn’t understand the choices made by previous developers. Anyway, we were paralyzed.&lt;/p&gt;
&lt;p&gt;We needed some sort of documentation explaining the rationale behind the different types of tests, a guide to select which one to write, some guidelines about how to write them.&lt;/p&gt;
&lt;p&gt;Test suites were organized by directories, each containing some type of test. As expected, some tests weren’t looking like the others in the same directory, but it was a start. We could have a README file in each directory explaining what tests should look like in there. This is the idea I wanted to talk about. It’s relatively simple and can probably avoid losing time, improve team speed, and remove some frustration.&lt;/p&gt;
&lt;p&gt;As an alternative, we could use &lt;a href=&quot;https://github.com/joelparkerhenderson/architecture-decision-record&quot;&gt;Architecture Decision Record&lt;/a&gt;. In my opinion, having the description file closer to the tests is better because it makes it easy to find when needed.&lt;/p&gt;
&lt;h2 id=&quot;what-should-we-find-in-the-readme-file&quot; tabindex=&quot;-1&quot;&gt;What should we find in the README file&lt;/h2&gt;
&lt;p&gt;The README file should be beneficial to newcomers, more junior developers, or even the ones who decided the test strategy, reminding us why we made these choices.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;capsule.png&quot; alt=&quot;Sandi Metz’s SUT as a capsule&quot;&gt;&lt;/p&gt;
&lt;p&gt;I really like &lt;a href=&quot;https://twitter.com/sandimetz&quot;&gt;Sandi Metz’s&lt;/a&gt; description of the &lt;a href=&quot;https://burritalks.io/talks/sandi-metz-the-magic-tricks-of-testing/?utm_medium=referral&amp;amp;utm_source=selrahcd.comm&amp;amp;utm_campaign=choripam_test_suite_guideline_article&quot;&gt;system under test as a capsule&lt;/a&gt; we can send incoming commands and queries to and which, in turn, can send outgoing queries and commands to something else.&lt;/p&gt;
&lt;p&gt;I think the test suite guide should explain how incoming messages are generated:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Are we allowed to call SUT methods directly?&lt;/li&gt;
&lt;li&gt;Or should we go through something that exercises the UI and help us pretend we’re a real user acting on a component?&lt;/li&gt;
&lt;li&gt;Should we send an HTTP request to an endpoint?&lt;/li&gt;
&lt;li&gt;Should we use framework provided tool to pretend we’ve sent an HTTP request?&lt;/li&gt;
&lt;li&gt;Should we call a CLI?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The guide should probably tell us what we should do about the other side of the capsule.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Are we allowed to stub dependencies? If so, which ones?&lt;/li&gt;
&lt;li&gt;Should we only stub dependencies doing IOs?&lt;/li&gt;
&lt;li&gt;Should we allow some IOs? Are database queries allowed but HTTP calls disallowed?&lt;/li&gt;
&lt;li&gt;Should a controller test exercise the domain code, or is it sufficient to know that we called some method or fired some command?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You probably can imagine a lot of variations just around these two themes.&lt;/p&gt;
&lt;p&gt;Justin Searl came up with other really interesting points we probably should include as well.&lt;/p&gt;
&lt;p&gt;The first one is a description of the confidence each type of test should give us. For instance, end-to-end tests verify that all pieces of the software are working well together. Adapter tests let us know if a contract with a dependency is still respected after an upgrade. Consumption tests prove that the behavior we are responsible for works correctly.&lt;/p&gt;
&lt;p&gt;Knowing the gained confidence of each test type helps decide which test to write. If we want to be confident about multiple things, maybe we need to use various kinds of tests.&lt;/p&gt;
&lt;p&gt;Note that it also drives, or enforces, the architectural choices. Say you want to ensure that an adapter behaves appropriately. We could use an end-to-end to prove that, and it would be costly to write, maintain, probably slower than needed, and could fail for multiple reasons. Once we decide that we gain confidence around adapter behavior using adapter tests, we will need the adapter to be testable on its own. To do this, we will probably add some boundaries around it.&lt;/p&gt;
&lt;p&gt;Another idea expressed in Justin’s talk is to know which understanding each type of test gives us.&lt;/p&gt;
&lt;p&gt;Here we’re more interested in feedback on the design of our application:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If our end-to-end tests are individually slow, maybe workflows are too complex for our users.&lt;/li&gt;
&lt;li&gt;If our consumption tests are hard to write, our service is probably hard to use, and we could improve the interface.&lt;/li&gt;
&lt;li&gt;If our contract tests with other teams are often failing, it might be a signal that our priorities aren’t aligned.&lt;/li&gt;
&lt;li&gt;Adapter tests give us clues about our usage of third parties, which in turn help us know what we should look for if we want to switch to another provider.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Justin also talks about indicating who the &amp;quot;user&amp;quot; is in each test. Is it a real user, another piece of code, our application talking to a dependency?&lt;br&gt;
It relates closely to the messages each test can send to the capsule.&lt;/p&gt;
&lt;p&gt;He also shares some guidelines and warning about each type of test. These could probably be included in the readme file too.&lt;/p&gt;
&lt;p&gt;Another valuable idea would be to keep one test always clean for each suite, following the rules and demonstrating the best practices. It would act as an exemplar we could refer to to get a better understanding of the content of the readme. Of course, the readme file links to this test file. This idea comes from the book &amp;quot;Living documentation&amp;quot; by &lt;a href=&quot;https://twitter.com/cyriux/&quot;&gt;Cyrille Martraire&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;in-short&quot; tabindex=&quot;-1&quot;&gt;In short&lt;/h2&gt;
&lt;p&gt;The readme file should guide us when deciding which test to write and how to write them.&lt;br&gt;
Some ideas of useful information to include are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which confidence are we getting from it&lt;/li&gt;
&lt;li&gt;Who is the user&lt;/li&gt;
&lt;li&gt;What are the incoming and outgoing messages&lt;/li&gt;
&lt;li&gt;What understanding are we getting from the test&lt;/li&gt;
&lt;li&gt;An exemplar of what’s considered a good test&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you need some help with your test I think I can help. Have a look at &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;my video course&lt;/a&gt; in French or &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let&#39;s have a chat&lt;/a&gt; and see what we can do together.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Comment tester une méthode privée ?</title>
      <link href="https://blog.charlesdesneuf.com/articles/comment-tester-une-methode-privee/"/>
      <updated>2022-09-20T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/comment-tester-une-methode-privee/</id>
      <summary>On ne la teste pas. Ça, c’est la réponse courte. La réponse longue demande un peu plus d’effort.</summary>
      <content type="html">&lt;p&gt;&lt;em&gt;Dans cet article quand j’utilise le terme &amp;quot;méthode privée&amp;quot; cela correspond aux méthodes qui ne sont pas accessibles depuis l’interface publique d’une classe. En PHP, par exemple, il peut s’agir de méthodes &lt;code&gt;private&lt;/code&gt; ou &lt;code&gt;protected&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;On ne la teste pas. Ça, c’est la réponse courte. La réponse longue demande un peu plus d’effort.&lt;/p&gt;
&lt;p&gt;Commençons par le commencement.&lt;/p&gt;
&lt;p&gt;La question originale est trompeuse. En réalité &lt;strong&gt;on ne cherche pas à tester une méthode&lt;/strong&gt;, quelle soit &lt;code&gt;private&lt;/code&gt;, &lt;code&gt;protected&lt;/code&gt; ou &lt;code&gt;public&lt;/code&gt;, &lt;strong&gt;mais un comportement&lt;/strong&gt;. On veut savoir ce que fait notre logiciel, pas comment il le fait, et donc la manière dont on implémente ce comportement nous importe assez peu.&lt;/p&gt;
&lt;p&gt;Évidemment, on va quand même retrouver une méthode publique comme point d’entrée du comportement, mais la méthode privée, elle, est un &lt;strong&gt;détail d’implémentation&lt;/strong&gt;. Le comportement qui nous intéresse pourrait tout aussi bien être servi par une méthode publique qui contient l’ensemble du code nécessaire, ou une méthode publique qui délègue une partie du travail à une ou plusieurs méthodes privées, ou même encore à une ou plusieurs autres méthodes publiques d’autres classes. En somme, &lt;strong&gt;l’existence ou non d’une méthode privée ne nous intéresse pas.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pour tester le comportement, on va donc se placer à l’endroit où l’on peut le déclencher et vérifier son bon déroulement, c’est-à-dire au niveau de la méthode publique. Au passage cela va &amp;quot;tester la méthode privée&amp;quot;, ou plus précisément &amp;quot;exercer le code contenu dans la méthode privée&amp;quot;. Encore une fois, on ne veut pas tester une méthode privée mais un comportement qui s’appuie sur une méthode privée. On peut dire que l&#39;&lt;strong&gt;on &amp;quot;teste&amp;quot; la méthode privée via une méthode publique.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;oui%2C-mais-cette-m%C3%A9thode-priv%C3%A9e-est-complexe&quot; tabindex=&quot;-1&quot;&gt;Oui, mais cette méthode privée est complexe&lt;/h2&gt;
&lt;p&gt;Ceci étant dit, il se peut que le code présent dans la méthode privée soit assez complexe pour que l’on ait envie de pouvoir exercer ce bout de code sans avoir à passer par la méthode publique. On veut ainsi éviter de devoir faire une mise en place de test complexe, d’avoir à zigzaguer au milieu du reste du code pour pouvoir se placer dans un cas particulier du comportement qui nous intéresse.&lt;/p&gt;
&lt;p&gt;Une solution couramment proposée est d’essayer de contourner le problème en rendant la méthode privée accessible. Ce qui peut se faire en utilisant le système de réflexion du langage. L’avantage de cette solution est que l’on n’a pas à toucher au code. En revanche on ajoute de la complexité à la mise en place de notre test et l’on augmente le couplage entre les tests et le code. Cela peut alors nuire à un potentiel futur refactoring, puisque les tests ont alors une connaissance assez fine de l’implémentation.&lt;/p&gt;
&lt;p&gt;Une autre solution, bien meilleure selon moi, est d’&lt;strong&gt;écouter les tests&lt;/strong&gt;. Comme très souvent, &lt;strong&gt;quand quelque chose est compliqué à tester ce ne sont pas les tests le problème mais la conception du code&lt;/strong&gt;.&lt;br&gt;
En nous montrant qu’un bout du comportement est complexe, si complexe que nous avons envie de pouvoir nous assurer de son bon fonctionnement séparément du reste &lt;strong&gt;les tests nous indiquent qu’il nous manque un concept&lt;/strong&gt;, qu’&lt;strong&gt;il manque un collaborateur&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Il faut alors prendre le temps de trouver quel est ce concept, de le définir, de le nommer. On peut ensuite &lt;strong&gt;introduire ce nouveau concept dans notre code à l’aide d’une nouvelle classe et y transférer la méthode privée, que l’on rend publique au passage&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Cela nécessite, certes, de modifier le code, mais permet de &lt;strong&gt;faire naitre un vocabulaire plus riche pour discuter du problème que l’on cherche à résoudre&lt;/strong&gt;. On est capable de documenter cette partie du comportement par elle-même au travers des tests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On n’a pas non plus besoin d’utiliser d’astuces qui complexifient les tests, un simple appel à la nouvelle méthode publique suffit.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On a réussi à transformer notre problème. Puisqu’il n’y a plus de méthode privée on n’a plus besoin d’essayer de tester de méthode privée.&lt;/p&gt;
&lt;p&gt;Voilà pourquoi la réponse longue à la question &amp;quot;Comment tester une méthode privée ?&amp;quot; est aussi &amp;quot;On ne la teste pas&amp;quot;.&lt;/p&gt;
&lt;p&gt;Et si vous voulez améliorer la manière dont vous écrivez des tests je ne peux que vous conseiller &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;ma formation vidéo sur l&#39;amélioration des tests automatisés&lt;/a&gt;. Avec cette formation vous saurez comment écrire des tests super lisibles, rapides, et maintenables. Il vous est aussi possible de &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;prendre rendez-vous&lt;/a&gt; et voir ensemble comment je peux vous apporter mon aide ou simplement pour discuter.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Le dummy, un indice pas si con à propos de votre design...</title>
      <link href="https://blog.charlesdesneuf.com/articles/le-dummy-un-indice-pas-si-con/"/>
      <updated>2022-09-28T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/le-dummy-un-indice-pas-si-con/</id>
      <summary>Un dummy dans un test est une bonne indication qu’il faut se poser des questions à propos du design du système sous test.</summary>
      <content type="html">&lt;p&gt;Je l’ai déjà évoqué dans &lt;a href=&quot;/articles/comment-tester-une-methode-privee/&quot;&gt;le précédent article&lt;/a&gt;, je suis persuadé que nos tests nous fournissent des informations pertinentes quant au design de nos applications. Il faut savoir les entendre.&lt;/p&gt;
&lt;p&gt;Pour &lt;a href=&quot;https://formation.charlesdesneuf.com/9e5b4913-ead7-4c2f-82c2-ac9a08b9a454&quot;&gt;une raison relativement évidente&lt;/a&gt;, je me suis retrouvé à expliquer les différences entre les différents types de doublures il y a peu. Cela m’a permis de me rendre compte que je n’utilisais que très rarement, pour ne pas dire jamais, de dummy, et ça m’a interrogé. Une question en entrainant une autre je me suis assez vite demandé dans quel cas un dummy est utile et ce que l’utilisation d’un dummy pouvait indiquer à propos du design du code.&lt;/p&gt;
&lt;h2 id=&quot;qu%E2%80%99est-ce-qu%E2%80%99un-dummy-%3F&quot; tabindex=&quot;-1&quot;&gt;Qu’est-ce qu’un dummy ?&lt;/h2&gt;
&lt;p&gt;Très rapidement, &lt;strong&gt;un dummy est un type de doublure qui sert à remplacer un collaborateur dont on ne va pas avoir besoin lors de l’exécution du test&lt;/strong&gt;. On s’en sert souvent pour remplacer un collaborateur pénible à construire mais pourtant nécessaire à la construction du système sous test. Un dummy peut aussi servir en tant que paramètre d’une méthode. Dans les deux cas ce collaborateur ne sert pas et on fait généralement en sorte qu’un appel fait sur le dummy provoque une erreur.&lt;/p&gt;
&lt;p&gt;Il existe plusieurs manières de créer un dummy, avec ou sans framework de mock, et les possibilités varient en fonction du langage que l’on utilise.&lt;/p&gt;
&lt;p&gt;Pour en savoir plus sur les dummies, je vous invite à consulter &lt;a href=&quot;http://xunitpatterns.com/Dummy%20Object.html&quot;&gt;la page de description de ce pattern sur le site de xUnit Patterns&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Une chose est sûre, les dummies nous parlent, et à nous d’essayer de les comprendre.&lt;/p&gt;
&lt;h2 id=&quot;un-dummy-comme-argument-pour-la-construction&quot; tabindex=&quot;-1&quot;&gt;Un dummy comme argument pour la construction&lt;/h2&gt;
&lt;p&gt;Intéressons-nous au premier cas, celui où l’on utilise un dummy pour remplacer une dépendance injectée à la construction.&lt;/p&gt;
&lt;p&gt;Pour avoir de quoi discuter prenons pour exemple une classe &lt;code&gt;Cuisinier&lt;/code&gt; qui nous permet d’allumer le gaz et de partir en pause.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Cuisinier&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; Gaziniè&lt;span class=&quot;token class-name type-declaration&quot;&gt;re&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$gaziniere&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Briquet&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$briquet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;PaquetDeCigarettes&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$paquetDeCigarettes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;allumerLeGaz&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;gaziniere&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ouvrirLeRobinet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$feu&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;briquet&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;allumerLeFeu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;gaziniere&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approcher&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$feu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;partirEnPause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$cigarette&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;paquetDeCigarettes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sortirUneCigarette&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$feu&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;briquet&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;allumerLeFeu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$cigarette&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;allumerAvec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$feu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;//...&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette classe a 3 dépendances :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une &lt;code&gt;Gazinière&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;un &lt;code&gt;Briquet&lt;/code&gt;,&lt;/li&gt;
&lt;li&gt;et un &lt;code&gt;PaquetDeCigarettes&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lorsque l’on va écrire des tests qui s’intéressent au premier comportement, l’allumage du gaz, seuls 2 collaborateurs vont nous êtres utiles, la &lt;code&gt;Gazinière&lt;/code&gt; et le &lt;code&gt;Briquet&lt;/code&gt;. Pour eux on va pouvoir utiliser de véritables collaborateurs ou une doublure autre qu’un dummy. Ici sans doute un mock. En revanche, le &lt;code&gt;PaquetDeCigarette&lt;/code&gt; ne nous intéresse absolument pas pour ce test et utiliser un dummy est une solution tout à fait acceptable.&lt;/p&gt;
&lt;p&gt;Pour le second comportement, celui où notre cuisinier part en pause, c’est pour la &lt;code&gt;Gazinière&lt;/code&gt; que l’on peut utiliser un dummy. Pas besoin du fourneau pour s’en griller une.&lt;/p&gt;
&lt;p&gt;Deux comportements, deux dummies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;La présence de ces deux dummies devrait selon moi nous pousser à nous interroger sur le design de notre application&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Est-ce que le design ne serait pas mieux en &lt;strong&gt;séparant les deux responsabilités de cette classe dans deux classes indépendantes&lt;/strong&gt; : un cuisinier spécialisé dans l’allumage de gaz et un cuisinier dont l’unique utilité est de prendre des pauses ?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Est-ce qu’avoir une classe &lt;code&gt;Cuisinier&lt;/code&gt; pour faire l’orchestration de ces actions a vraiment du sens ?&lt;/strong&gt; Ne devrait-on pas se débarrasser du &lt;code&gt;Cuisinier&lt;/code&gt; et créer un concept de gazinière toujours prête à l’emploi, quitte à ce que celle-ci possède son propre &lt;code&gt;Briquet&lt;/code&gt; ? Avoir un paquet de cigarettes qui fournit des cigarettes qui peuvent s’allumer sans besoin de taxer du feu ?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Il n’y a pas de réponse évidente à ces questions. Le code environnant, les préférences personnelles, les habitudes de l’équipe, et sans doute bien d’autres facteurs jouent sur les réponses que l’on peut apporter.&lt;/p&gt;
&lt;h2 id=&quot;oui-mais-le-briquet-!&quot; tabindex=&quot;-1&quot;&gt;Oui mais le briquet !&lt;/h2&gt;
&lt;p&gt;De manière intéressante &lt;strong&gt;la métrique Lack Of Cohesion in Method version 4, ou &lt;a href=&quot;https://objectscriptquality.com/docs/metrics/lack-cohesion-methods-lcom4&quot;&gt;&lt;code&gt;LCOM4&lt;/code&gt;&lt;/a&gt; de cette classe est de 1, ce qui semble indiquer un bon niveau de cohésion&lt;/strong&gt;. C’est l’utilisation du &lt;code&gt;Briquet&lt;/code&gt; dans les deux méthodes qui permet à cette classe d’être perçue comme cohésive.&lt;/p&gt;
&lt;p&gt;La présence de dummies nous offre ici un signal que la métrique n’est pas en mesure de donner et nous permet de poser de nouvelles questions :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Est-ce que l’utilisation du &lt;code&gt;Briquet&lt;/code&gt; dans les deux méthodes est une justification suffisante pour placer les deux comportements dans la même classe ?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Est-ce qu’on allume vraiment une cigarette comme on allume une gazinière ?&lt;/li&gt;
&lt;li&gt;Est-ce que ce &lt;code&gt;Briquet&lt;/code&gt; n’est pas uniquement une possible implémentation de &lt;code&gt;SystèmeDAllumageDeGazinière&lt;/code&gt; ? D’autres implémentations pouvant être &lt;code&gt;Alumette&lt;/code&gt; ou &lt;code&gt;AllumagePiezoElectrique&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Est-ce qu’on peut allumer une cigarette avec un &lt;code&gt;AllumagePiezoElectrique&lt;/code&gt; ? (non).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Est-ce qu’avoir deux systèmes d’allumage n’aurait pas plus de sens ?&lt;/strong&gt; Un pour la cigarette et un pour la gazinière ? Si oui, le &lt;code&gt;LCOM4&lt;/code&gt; de la classe passe à 2, signal qu’une séparation en 2 de la classe pourrait être intéressante.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Doit-on changer ce code ?&lt;/p&gt;
&lt;p&gt;Oui, non, peut-être, je ne sais pas. À vous de voir, finalement.&lt;/p&gt;
&lt;p&gt;Ce que je trouve intéressant, c’est que &lt;strong&gt;la présence d’un dummy nous offre un signal&lt;/strong&gt;. Il nous pousse à &lt;strong&gt;nous interroger sur la répartition des responsabilités entre les différents collaborateurs&lt;/strong&gt; :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Doit-on splitter certaines classes en plusieurs ?&lt;/li&gt;
&lt;li&gt;Doit-on changer la manière dont les classes collaborent entre-elles ?&lt;/li&gt;
&lt;li&gt;Doit-on transférer certaines responsabilités d’une classe à une autre ? Ou dans un nouveau concept ?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Encore une fois, je ne pense pas qu’il y ait de réponse toute faite pour ces questions. La forme des tests, comme les résultats de métriques, n’est qu’un outil pour nous pousser à nous poser des questions mais c’est bien à un développeur ou une développeuse de faire l’effort de choisir ce qui lui semble le meilleur design au moment actuel.&lt;/p&gt;
&lt;p&gt;Dans &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;ma formation vidéo sur l&#39;amélioration des tests automatisés&lt;/a&gt; nous explorons les différents types de doublures. Il vous est aussi possible de &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;prendre rendez-vous&lt;/a&gt; et voir ensemble comment je peux vous apporter mon aide ou simplement pour discuter.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Des tests super lisibles</title>
      <link href="https://blog.charlesdesneuf.com/articles/des-tests-super-lisibles/"/>
      <updated>2022-10-10T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/des-tests-super-lisibles/</id>
      <summary>Quelques astuces pour fortement améliorer la lisibilité des tests</summary>
      <content type="html">&lt;p&gt;i_Cet article est une traduction et légère adaptation d’&lt;a href=&quot;/articles/extra-readable-tests/&quot;&gt;un article précédent&lt;/a&gt; écrit il y a déjà plusieurs années sur ce même blog. Les techniques exposées ici me sont toujours utiles et je pense qu’elles peuvent aider un grand nombre de personnes à améliorer leurs tests automatisés. Bonne lecture !_&lt;/p&gt;
&lt;p&gt;Avec cet article je souhaite partager certaines des techniques que j’utilise régulièrement lorsque j’écris des tests. Ces techniques permettent de fortement &lt;strong&gt;améliorer la lisibilité des tests&lt;/strong&gt;, les rendant plus faciles à comprendre. Certaines d’entre elles ont également un &lt;strong&gt;impact positif sur la maintenabilité de l’ensemble des tests&lt;/strong&gt;. On verra un exemple dans l’article.&lt;/p&gt;
&lt;p&gt;D’autres personnes ont déjà partagé certaines de ces idées et cet article, ainsi que ma manière d’aborder les tests, s’appuie en partie sur les travaux de &lt;a href=&quot;https://www.youtube.com/watch?v=XHnuMjah6ps&quot;&gt;Sandro Mancuso&lt;/a&gt;, &lt;a href=&quot;https://www.youtube.com/watch?v=1_dpOZmKXBw&quot;&gt;Matthias Verraes&lt;/a&gt;, &lt;a href=&quot;https://dannorth.net/introducing-bdd/&quot;&gt;Dan North&lt;/a&gt;, et également sur des idées exposées par Nat Pryce et Steve Freeman dans &lt;a href=&quot;http://www.growing-object-oriented-software.com/&quot;&gt;Growing Oriented Object Software Guided By Tests&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Dans cet article, nous allons refactorer le test suivant une étape après l’autre afin de le rendre davantage lisible.&lt;br&gt;
Ici on utilise &lt;a href=&quot;https://phpunit.de/&quot;&gt;PhpUnit&lt;/a&gt; mais &lt;strong&gt;les idées partagées ici peuvent l’être avec d’autres frameworks&lt;/strong&gt; (certaines des idées sont d’ailleurs inspirées par &lt;a href=&quot;http://www.phpspec.net/&quot;&gt;PhpSpec&lt;/a&gt; qui est également un très bon outil) ou même &lt;strong&gt;dans d’autres langages que PHP&lt;/strong&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;SendWelcomeEmailToMemberTest&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPUnit_Framework_TestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;tearDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WelcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldReceive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;           &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;nom-du-test-en-snake_case&quot; tabindex=&quot;-1&quot;&gt;Nom du test en Snake_case&lt;/h2&gt;
&lt;p&gt;Comme vous le voyez dans ce test on n’utilise pas la convention de nommage classique en camelCase, &lt;code&gt;testWhatItsSupposedToDo&lt;/code&gt;, mais plutôt du snake-case. De plus le nom de la méthode de test débute par &lt;code&gt;it_&lt;/code&gt;, ce qui, nous oblige à utiliser l’annotation &lt;code&gt;@test&lt;/code&gt;. &lt;strong&gt;Démarrer le nom du test avec &lt;code&gt;it_&lt;/code&gt; aide à formuler une phrase qui décrit le comportement que l’on veut tester dans un langage naturel&lt;/strong&gt;. Il est alors possible d’utiliser la liste de tous les tests pour générer une documentation, ce qui se fait facilement avec &lt;a href=&quot;https://phpunit.readthedocs.io/fr/latest/textui.html#testdox&quot;&gt;l’option testdox de PhpUnit&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;arrange---act---assert&quot; tabindex=&quot;-1&quot;&gt;Arrange - Act - Assert&lt;/h2&gt;
&lt;p&gt;Le premier changement est de réorganiser le test pour qu’il suive la forme &amp;quot;Arrange - Act - Assert&amp;quot;. Lorsque tous les tests sont sous cette forme il est très simple de savoir quelles sont les préconditions du test - ce que j’appelle la mise en place du petit monde -, l’action, et les assertions, qui sont toujours à la fin du scénario.&lt;/p&gt;
&lt;p&gt;Dans la version actuelle, une des assertions, celle à propos de l’envoi de l’email, est faite avant l’étape d’action.&lt;/p&gt;
&lt;p&gt;Pour permettre de déplacer l’assertion à la fin du test on a besoin d’utiliser un &lt;em&gt;Spy&lt;/em&gt; à la place d’un &lt;em&gt;Mock&lt;/em&gt;. Pour en savoir plus sur les différents types de doublures vous pouvez consulter &lt;a href=&quot;https://martinfowler.com/bliki/TestDouble.html&quot;&gt;cet article&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// Arrange    &lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// Utilisation d’un spy à la place d’un mock&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WelcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// Act&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// Assert&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldHaveReceived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;                &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;builder&quot; tabindex=&quot;-1&quot;&gt;Builder&lt;/h2&gt;
&lt;p&gt;Faisons maintenant un petit effort d’imagination : la classe &lt;code&gt;Member&lt;/code&gt; est une des classes centrales de notre application et on la retrouve dans de nombreux tests. Dans ces tests, on utilise le constructeur de &lt;code&gt;Member&lt;/code&gt; pour instancier des membres.&lt;/p&gt;
&lt;p&gt;Un jour arrive une nouvelle fonctionnalité qui nous pousse à vouloir ajouter la notion de date de naissance pour les membres. On ajoute alors un nouveau paramètre &lt;code&gt;$birthDate&lt;/code&gt; au constructeur de la classe &lt;code&gt;Member&lt;/code&gt;. Le problème, c’est que &lt;strong&gt;cela va faire échouer tous les tests qui font un appel à ce constructeur&lt;/strong&gt;. En route pour la correction de dizaines de tests. Quelle joie !&lt;/p&gt;
&lt;p&gt;Pour éviter ce problème ma solution favorite est d’utiliser un &lt;a href=&quot;https://en.wikipedia.org/wiki/Builder_pattern&quot;&gt;builder&lt;/a&gt; qui va encapsuler l’appel au constructeur de &lt;code&gt;Member&lt;/code&gt;. Désormais quand on veut effectuer un changement sur le constructeur il n’est plus nécessaire de passer dans chacun des tests. Seul un changement dans la méthode &lt;code&gt;build&lt;/code&gt; du builder est nécessaire.&lt;/p&gt;
&lt;p&gt;En utilisant un builder pour &lt;code&gt;Member&lt;/code&gt; et &lt;code&gt;Email&lt;/code&gt; le test est alors le suivant :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WelcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withFirstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withEmailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeEmail&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withSubject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldHaveReceived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$welcomeEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Et voici le code du &lt;code&gt;MemberBuilder&lt;/code&gt; dans une version simple :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;MemberBuilder&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$firstname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$emailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$birthDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$faker&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name class-name-fully-qualified static-context&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Faker&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Factory&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;emailAddress&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$faker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;firstname&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$faker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;firstname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;birthDate&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$faker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name return-type&quot;&gt;Member&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;         &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;firstname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;         &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;emailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;         &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;birthDate&lt;/span&gt;&lt;br&gt;         &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;withFirstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$firstname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;firstname&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$firstname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;withEmailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$emailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;emailAddress&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$emailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;bornOn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$birthDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;birthDate&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$birthDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;En plus d’encapsuler la création d’une instance de &lt;code&gt;Member&lt;/code&gt; a un unique endroit l’utilisation d’un builder à un second avantage. Dans le constructeur du builder une valeur par défaut est assignée pour chacune des informations nécessaires à la création d’un membre. Cela nous permet de n’avoir à &lt;strong&gt;spécifier que les informations pertinentes pour le test que l’on est en train d’écrire&lt;/strong&gt;. Si on a besoin d’un &lt;code&gt;Member&lt;/code&gt; et que seul son prénom nous intéresse on peut appeler &lt;code&gt;(new MemberBuilder)-&amp;gt;withFirstName(&#39;John&#39;)-&amp;gt;build()&lt;/code&gt; et omettre de préciser son adresse email et sa date de naissance. &lt;strong&gt;Cela permet de réduire le bruit lors de la mise en place du test, ce qui le rend plus facile à comprendre.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;De plus ici on utilise &lt;a href=&quot;https://github.com/fzaninotto/Faker&quot;&gt;Faker&lt;/a&gt;, une library qui permet de créer de fausses données de manière aléatoire. En utilisant des données aléatoires on réduit le risque que notre implémentation ne se base sur des valeurs fixes.&lt;/p&gt;
&lt;h2 id=&quot;fonction-de-cr%C3%A9ation-pour-les-builders&quot; tabindex=&quot;-1&quot;&gt;Fonction de création pour les builders&lt;/h2&gt;
&lt;p&gt;Pour faciliter encore davantage la lisibilité du test, il est possible de masquer la création des builders derrières des fonctions.&lt;br&gt;
En introduisant les deux fonctions suivantes :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;aMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MemberBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;anEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EmailBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On peut modifier le test sous cette forme :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WelcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;aMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Création d’un Member&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withFirstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withEmailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$welcomeEmail&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;anEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Création d’un Email&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withSubject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldHaveReceived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$welcomeEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;m%C3%A9thodes-d%E2%80%99aide&quot; tabindex=&quot;-1&quot;&gt;Méthodes d’aide&lt;/h2&gt;
&lt;p&gt;Pour nous permettre d’employer le vocabulaire du métier, ou a minima nous permettre d’utiliser un langage plus proche du langage naturel, il est aussi possible d’ajouter des méthodes d’aide :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;SendWelcomeEmailToMemberTest&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PHPUnit_Framework_TestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;setUp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;mailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;MailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;welcomeMailSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;WelcomeMailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;mailSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;tearDown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// it exists a member with first name &#39;Charles&#39;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// and with email adress &#39;charles@test.fr&#39;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;it_exists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withFirstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withEmailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// It should send an email from &#39;us@chorip.am&#39; to &#39;charles@test.fr&#39;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// with subject &#39;Welcome&#39; and with content &#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;it_should_send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;anEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withSubject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_exists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;MemberBuilder&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$member&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_should_send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;EmailBuilder&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;mailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shouldHaveReceived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;send&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name static-context&quot;&gt;Mockery&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$email&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;once&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le test se lit alors bien plus aisément et des personnes non techniques serait tout à fait en mesure de comprendre le comportement décrit par elles-mêmes. &lt;strong&gt;Le test peut désormais servir de documentation aussi bien pour les développeuses et développeurs mais aussi pour le reste de l’équipe.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;aller-plus-loin-avec-une-extension-pour-phpunit&quot; tabindex=&quot;-1&quot;&gt;Aller plus loin avec une extension pour PhpUnit&lt;/h2&gt;
&lt;p&gt;On pourrait encore améliorer la lisibilité du test en replaçant l’utilisation de &lt;code&gt;$this&lt;/code&gt; pour appeler les méthodes que nous venons d’introduire par &lt;code&gt;given()&lt;/code&gt; et &lt;code&gt;then()&lt;/code&gt;. J’ai tenté une expérimentation qui fonctionne mais n’ai jamais pris le temps de pousser l’idée jusqu’à créer une extension pour PhpUnit qui permette de le faire facilement.&lt;/p&gt;
&lt;p&gt;Voilà ce à quoi ressemble le test en appliquant cette idée :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test&lt;br&gt;     */&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;it_sends_a_welcome_email_to_a_member&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;given&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;it_exists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;aMember&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withFirstName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withEmailAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;welcomeMailSender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;sendTo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$charles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;it_should_send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;anEmail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;us@chorip.am&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;charles@test.fr&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withSubject&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Welcome&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;withContent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Hey! Welcome!&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Toutes &lt;strong&gt;ces techniques peuvent être utilisées indépendamment les unes des autres&lt;/strong&gt;. On peut les introduire pas à pas, &lt;strong&gt;en fonction des problèmes que l’on rencontre&lt;/strong&gt; ou des améliorations que l’on souhaite apporter, et nous permettent d’obtenir des tests très facilement lisibles une fois combinées.&lt;/p&gt;
&lt;p&gt;Comme pour chaque outil il y a bien sûr un cout d’introduction qui doit être mis en regard de l’intérêt du projet sur lequel on est en train de travailler. Je dois admettre que de mon côté, j’essaye de mettre en place des builders le plus vite possible. &lt;strong&gt;Ils améliorent énormément la lisibilité des tests, facilitent l’écriture des tests et leur maintenabilité au cours du temps&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Quoiqu’il en soit je vous encourage à garder ces techniques en tête pour pouvoir vous en servir en case de besoin et pour vous permettre d’avoir des tests avec lesquels vous preniez plaisir à travailler pendant un bon bout de temps !&lt;/p&gt;
&lt;p&gt;Si vous voulez aller encore plus loin, je vous conseille &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;ma formation vidéo sur l&#39;amélioration des tests automatisés&lt;/a&gt; dans laquelle nous allons plus en profondeur sur des techniques permettant d&#39;écrire des tests extrèmement lisibles et facilement maintenables. Il nous est aussi possible de &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;nous rencontrer&lt;/a&gt; et voir ensemble comment je peux vous apporter mon aide.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Des solutions pour les conflits au niveau de la base de données pour les tests exécutés en parallèle</title>
      <link href="https://blog.charlesdesneuf.com/articles/tests-en-parallele-et-base-de-donnees/"/>
      <updated>2022-10-25T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/tests-en-parallele-et-base-de-donnees/</id>
      <summary>Découvrez 3 stratégies efficaces pour résoudre les conflits de base de données lors de l&#39;exécution de tests en parallèle et améliorer la fiabilité de vos tests.</summary>
      <content type="html">&lt;p&gt;On m’a demandé il y a quelques jours si j’avais des ressources parlant de solution aux conflits entre tests joués en parallèle lorsqu’ils touchent à la base de données. J’avais quelques pistes à partager et je me suis dit que ça ferait un bon sujet d’article. Le voici donc.&lt;/p&gt;
&lt;h2 id=&quot;le-probl%C3%A8me&quot; tabindex=&quot;-1&quot;&gt;Le problème&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./tests-conflict.png&quot; alt=&quot;Trois processus de tests qui s’exécutent en parallèle. Deux tests provenant de deux processus différents entrent en conflit au niveau de la base de données&quot;&gt;&lt;/p&gt;
&lt;p&gt;Lorsque l’on joue des tests en parallèle on se retrouve face à des problèmes qui n’existaient pas lorsque les tests étaient joués en série, notamment lorsque les tests partagent une ressource, comme une base de données.&lt;/p&gt;
&lt;p&gt;Des tests qui, jusque-là, tournaient de manière régulière et avaient le même comportement à chaque fois qu’ils étaient exécutés commencent à avoir des comportements différents d’un run de test à un autre, parfois ils passent, parfois ils échouent. &lt;strong&gt;On parle de flaky tests pour décrire les tests qui vont passer ou échouer en fonction de certaines conditions.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Prenons un exemple avec la base de données :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nous avons deux tests qui ajoutent un utilisateur dans la base de données lorsqu’ils sont lancés.&lt;/li&gt;
&lt;li&gt;Bien sûr, le nom de l’utilisateur doit être unique.&lt;/li&gt;
&lt;li&gt;Les choses sont bien faites et après chaque test l’utilisateur est supprimé de la base de données, ce qui permet aux différents tests d’utiliser le même nom d’utilisateur.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ces tests vont fonctionner parfaitement en série. En revanche en parallèle il est possible que l’un d’eux échoue s’il essaye d’ajouter l’utilisateur à la base de données avant que l’autre test n’ait eu le temps de faire le ménage.&lt;/p&gt;
&lt;p&gt;Nos deux tests qui étaient, jusque-là, reproductibles deviennent flaky.&lt;/p&gt;
&lt;p&gt;Il existe, bien évidemment, plusieurs solutions à ce problème.&lt;/p&gt;
&lt;h2 id=&quot;bien-pr%C3%A9parer-les-donn%C3%A9es&quot; tabindex=&quot;-1&quot;&gt;Bien préparer les données&lt;/h2&gt;
&lt;p&gt;Commençons par la fausse bonne idée.&lt;/p&gt;
&lt;p&gt;Une solution est de faire en sorte de préparer les jeux de données qui vont être utilisés dans les tests pour éviter les collisions. Cette solution semble simple à première vue mais risque de poser des problèmes de maintenance des tests sur le long terme.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Il va falloir s’assurer que certaines données ne sont utilisées qu’une seule fois et devenir imaginatif pour créer de nouvelles données.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On peut imaginer mettre en place des outils qui permettent de connaitre quelles sont les données déjà utilisées pour ne pas s’en resservir, mais &lt;strong&gt;ces outils risquent de devenir complexes assez vite&lt;/strong&gt;. Il ne s’agit là, à mon avis, que d’une fuite en avant.&lt;/p&gt;
&lt;p&gt;On peut également se dire que l’on peut lancer les tests pour savoir si chaque nouveau test n’entre pas en conflit avec un test précédent mais malheureusement il est possible que le conflit ne soit pas détectable immédiatement et le devienne plus tard.&lt;/p&gt;
&lt;p&gt;Bref, cette option est donc clairement à déconseiller.&lt;/p&gt;
&lt;h2 id=&quot;faire-tourner-les-tests-en-s%C3%A9ries&quot; tabindex=&quot;-1&quot;&gt;Faire tourner les tests en séries&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./tests-serie.png&quot; alt=&quot;Les tests sont exécutés en série dans un seul processus.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Cette solution peut sembler décevante mais a le mérite d’exister : &lt;strong&gt;Si les tests n’étaient pas flaky lorsqu’ils tournaient en séquence une solution est d’abandonner la parallélisation.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Dans cette solution on échange du temps d’exécution contre davantage de fiabilité. Si le temps d’exécution sans la parallélisation n’est pas trop important il s’agit sans doute de la solution la plus simple.&lt;/p&gt;
&lt;p&gt;Mais lorsque l’on met en place de la parallélisation c’est parce que la durée d’exécution des tests était trop importante et que l’on veut aller plus vite.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Ce qui fait que cette solution risque de ne pas satisfaire tout le monde et nous mène l’option suivante.&lt;/p&gt;
&lt;h2 id=&quot;se-passer-de-la-base-de-donn%C3%A9es&quot; tabindex=&quot;-1&quot;&gt;Se passer de la base de données&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./tests-parallel-fakes.png&quot; alt=&quot;Les tests sont exécutés en parallèle mais ne communiquent plus avec la base de données. À la place ils utilisent des fakes.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Puisque le problème vient du partage de la base de données entre plusieurs tests une autre solution est de se passer de la base de données pour les tests. &lt;strong&gt;On peut imaginer remplacer les différentes dépendances qui s’appuient sur une base de données par des doublures qui vont avoir le même comportement que la dépendance originale mais ne vont pas avoir besoin de partager de ressource.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Autrement dit il est possible d’utiliser un fake par processus, tel qu’un repository en mémoire, qui va stocker les données dans un tableau. &lt;strong&gt;Une instance de ce repository sera créée dans chaque processus, ou même mieux pour chaque test, ce qui va éviter les conflits entre les tests.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Mais là encore cette solution a quelques inconvénients, le premier étant que l’&lt;strong&gt;on aura toujours besoin de tests avec la base de données pour s’assurer que l’intégration de notre code avec celle-ci se passe comme voulu&lt;/strong&gt; (via des tests end-2-end, des tests de contrat qui s’assurent que notre doublure a bien le même comportement que la dépendance remplacée, ou autre...). Ces tests peuvent eux aussi potentiellement rencontrer des problèmes lorsqu’ils sont exécutés en parallèle. Retour à la case départ en quelque sorte.&lt;/p&gt;
&lt;p&gt;Sauf que tout n’est pas perdu : si l’on a réussi à réduire le nombre de tests qui dépendent de la base de données &lt;strong&gt;il y a des chances que notre suite de tests tourne beaucoup plus rapidement et l’on peut alors faire tourner nos tests en séries&lt;/strong&gt;. L’option précédente devient viable.&lt;/p&gt;
&lt;p&gt;Le second inconvénient est la potentielle difficulté de mise en place de cette solution. &lt;strong&gt;Transformer l’architecture d’une application pour permettre de pouvoir remplacer certaines dépendances pendant les tests n’est pas forcément facile et peut prendre beaucoup de temps.&lt;/strong&gt; C’est pourquoi il peut s’agir de la solution la plus compliquée à mettre en œuvre suivant l’état du système.&lt;/p&gt;
&lt;h2 id=&quot;utiliser-des-transactions&quot; tabindex=&quot;-1&quot;&gt;Utiliser des transactions&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./tests-transaction.png&quot; alt=&quot;Les tests sont exécutés en parallèle dans une transaction, ce qui évite les conflits.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Fort heureusement il y a une solution généralement plus rapide à mettre en place : &lt;strong&gt;démarrer une transaction avant le début du test et faire un rollback une fois le test terminé&lt;/strong&gt;, que ce soit un succès ou non. Chaque test est alors exécuté dans son bac à sable, sur la même base de données, et ne peut pas interagir avec les autres.&lt;/p&gt;
&lt;p&gt;Dans le monde PHP la plupart des frameworks proposent une solution pour ça, soit nativement, soit via des dépendances à installer&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. Il est également possible de faire sa propre implémentation.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt; Cette solution nécessite en général beaucoup moins de travail pour pouvoir être mise en place que la précédente.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Au passage, cette solution permet d’ailleurs de régler des problèmes d’interactions entre tests également lorsqu’ils sont lancés en série&lt;/strong&gt;, sans avoir besoin de se préoccuper du nettoyage d’après test.&lt;/p&gt;
&lt;p&gt;Évidemment, vous vous en doutez bien, cette solution a, elle aussi, un inconvénient. Pour fonctionner &lt;strong&gt;il faut que le test et le système sous test partagent la même connexion à la base de données&lt;/strong&gt;. Si ce n’est pas le cas le test n’est pas en mesure démarrer et d’annuler la transaction. En fonction de l’architecture de l’application, il peut être compliqué de réussir à mettre la main sur la même connexion aussi bien dans le code de production que dans les tests. De plus, cette contrainte rend cette solution inefficace pour certains types de tests qui vont nécessairement devoir séparer le processus dans lequel tournent les tests du processus dans lequel tourne l’application - les tests end-2-end par exemple.&lt;/p&gt;
&lt;p&gt;Heureusement, il reste encore une alternative...&lt;/p&gt;
&lt;h2 id=&quot;utiliser-une-db-par-processus&quot; tabindex=&quot;-1&quot;&gt;Utiliser une DB par processus&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./tests-parallel-multi-db.png&quot; alt=&quot;Chaque processus lancé en parallèle bénéficie d’une base de données dédiée.&quot;&gt;&lt;/p&gt;
&lt;p&gt;La dernière solution permet de répondre à la contrainte des processus différents entre le test et l’application, et également à toutes situations dans lesquelles on ne peut pas, ou l’on ne veut pas, mettre en place le système de transaction.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Plutôt que d’essayer de faire interagir les tests des différents processus avec la même base de données on va dédier une base de données à chacun.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;En fonction du runner de test la mise en place peut-être plus ou moins compliquée.&lt;/p&gt;
&lt;p&gt;Certains runners créent un identifiant incrémental pour chaque processus et donnent accès à cet identifiant via une variable d’environnement.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;&lt;br&gt;
Il est alors possible d’utiliser la variable d’environnement pour créer la connexion à la base de données dédiée au processus courant.&lt;/p&gt;
&lt;p&gt;D’autres runners de test ne semblent pas fournir de variable d’environnement&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;. Il faut alors générer un identifiant unique dans un hook du runner de test qui va s’exécuter avant le début de tests et l’ajouter aux variables d’environnement. Et enfin, de la même manière, utiliser la variable d’environnement pour se connecter à la base de données.&lt;/p&gt;
&lt;p&gt;Reste encore à créer les bases de données. Dans le cas de la variable d’environnement incrémentée il est possible de créer les différentes bases avant le début des tests pour peu que l’on connaisse le nombre de processus qui vont tourner en parallèle. C’est la solution simple.&lt;/p&gt;
&lt;p&gt;Alternativement, si l’identifiant est aléatoire ou que le nombre de processus ne peut être connu apriori, il est possible de créer la base de données avant l’exécution des tests, là encore dans un hook. Particulièrement dans le cas d’un identifiant aléatoire, il faut penser à supprimer la base de données qui vient d’être créée à la fin des tests pour éviter de faire une collection de bases inutilisées.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;À noter qu’il est possible que cette solution soit plus rapide à mettre en place que la solution avec les transactions selon l’état du système. Une fois le système de création de bases de données installé il n’y a pas besoin de faire de nombreux changements. Il faut uniquement modifier la création de la connexion pour indiquer la base de données à utiliser.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./tests-e2e-multi-instance.png&quot; alt=&quot;Chaque processus de tests communique avec une instance dédiée de l’application qui elle-même communique avec sa propre base de données.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Dans le cas des tests end-2-end l’idée va rester la même, une base de données par processus de tests, mais la mise en pratique va devoir être un peu différente. Il va falloir permettre aux tests d’indiquer à quelle base de données ils sont rattachés. Cela peut se faire en utilisant plusieurs instances de l’application, chacune connectée à une base de données différentes. Chaque processus de test va alors pouvoir appeler l’instance qui lui est dédiée (ex.: en changeant de nom de domaine ou de port).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./tests-e2e-one-instance.png&quot; alt=&quot;Tous les processus de tests communiquent avec la même instance de l’application et celle-ci décide de sélectionne la base de données en fonction d’informations transmises par le test.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Une autre solution, s’il n’est pas possible d’avoir plusieurs instances de l’application, est que le test communique le nom de la base de données à laquelle il souhaite s’adresser (ex.: au travers d’un header) à celle-ci, et qu’en fonction de l’information transmise elle se connecte à la base de données. Là encore cette solution présente des inconvénients, principalement liés à la mise en place de logique dédiée spécialement aux tests pour pouvoir changer de base de données.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Comme souvent, il n’y a pas une seule solution qui va convenir à toutes les situations. Avec cet article, on a vu différentes pistes de solutions, qui peuvent répondre à différents cas d’usage, et qui sont plus ou moins faciles à mettre en place selon l’état de l’application.&lt;/p&gt;
&lt;p&gt;Et donc comme toujours, pas de solution miracle, c’est à vous de voir laquelle de ces options s’applique le mieux à votre contexte.&lt;/p&gt;
&lt;p&gt;Il existe peut-être d’autres solutions à ce type de problème. Si vous en connaissez n’hésitez pas à me contacter &lt;a href=&quot;https://www.charlesdesneuf.com/contact/&quot;&gt;ici&lt;/a&gt; ou sur &lt;a href=&quot;http://twitter.com/Selrahcd&quot;&gt;Twitter&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Et si vous voulez faire en sorte d&#39;avoir des tests avec lesquels vous prendrez un réel plaisir à travailler, vous pouvez accéder à &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;ma formation vidéo sur l&#39;amélioration des tests automatisés&lt;/a&gt;. Il vous est aussi possible de &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;prendre un rendez-vous&lt;/a&gt; pour que nous travaillions ensemble à faire passer votre équipe au niveau supérieur.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Certainement pas pour tester, pour le fun, ou simplement parce que l’outil le permet. Non, non... &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Par exemple &lt;a href=&quot;https://laravel.com/docs/5.4/database-testing#using-transactions&quot;&gt;Laravel l’inclut nativement&lt;/a&gt;, côté Symfony il faut installer &lt;a href=&quot;https://github.com/dmaicher/doctrine-test-bundle&quot;&gt;un plug-in pour Doctrine&lt;/a&gt;. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Voici un exemple d’implémentation en PHP avec PhpUnit et PDO qui n’est pas de toute beauté, mais qui donne une idée de ce qui peut être fait avec &lt;a href=&quot;https://github.com/SelrahcD/parallel-with-db/blob/transactions/tests/Tools/DatabaseConnection.php&quot;&gt;une &amp;quot;encapsulation&amp;quot; de la connexion&lt;/a&gt; et &lt;a href=&quot;https://github.com/SelrahcD/parallel-with-db/blob/transactions/tests/Tools/RunTestInTransaction.php&quot;&gt;une extension PhpUnit&lt;/a&gt;. &lt;a href=&quot;#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;C’est le cas de &lt;a href=&quot;https://github.com/paratestphp/paratest#test-token&quot;&gt;Paratest&lt;/a&gt; côté PHP, et de &lt;a href=&quot;https://jestjs.io/fr/docs/environment-variables#jest_worker_id&quot;&gt;Jest&lt;/a&gt; dans le monde JS. &lt;a href=&quot;#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;xUnit, côté .Net ne semble pas partager de variable d’environnement avec un ID pour le processus. &lt;a href=&quot;https://www.fusonic.net/de/blog/fast-unit-tests-with-databases-part-1&quot;&gt;Cette série d’articles&lt;/a&gt; donne une solution en C# avec Entity Framework. &lt;a href=&quot;#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Un &lt;a href=&quot;https://github.com/SelrahcD/parallel-with-db/blob/no-env-var/tests/Tools/CreateDBForEachProcess.php&quot;&gt;exemple de hook&lt;/a&gt; qui permet de créer et de supprimer une base de données avec un nom aléatoire en PHP. &lt;a href=&quot;#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Le tags, la fonctionnalité oubliée des lanceurs de tests</title>
      <link href="https://blog.charlesdesneuf.com/articles/tags-fonctionnalite-oubliee-des-lanceurs-de-tests/"/>
      <updated>2022-11-02T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/tags-fonctionnalite-oubliee-des-lanceurs-de-tests/</id>
      <summary>Quelques exemples d’utilisation des tags qui vont changer votre vision des tests.</summary>
      <content type="html">&lt;p&gt;Il y a une fonctionnalité des lanceurs de tests que l’on utilise bien trop peu, moi le premier : les tags.&lt;/p&gt;
&lt;p&gt;Avec un peu d’imagination ils peuvent pourtant nous rendre de chouettes services, et voir même changer certaines discussions autour des tests et de leur organisation.&lt;/p&gt;
&lt;h2 id=&quot;c%E2%80%99est-quoi-les-tags-%3F&quot; tabindex=&quot;-1&quot;&gt;C’est quoi les tags ?&lt;/h2&gt;
&lt;p&gt;Habituellement pour créer des groupes de tests, on se base sur la position du test au milieu de notre ensemble de tests. Les tests sont groupés car ils appartiennent à la même class de tests, à la même méthode &lt;code&gt;describe&lt;/code&gt;, au même fichier, à une même hiérarchie de fichiers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Les tags sont un autre moyen de créer des groupes de tests sans avoir besoin de savoir où ils sont rangés.&lt;/strong&gt; Certains outils parlent d’ailleurs de groupes plutôt que de tags.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./annotation.png&quot; alt=&quot;Un test avec une annotation @group database&quot;&gt;&lt;/p&gt;
&lt;p&gt;Généralement, les tags se mettent en place via le mécanisme d’annotations du langage dans lequel sont écrits les tests.&lt;/p&gt;
&lt;p&gt;Une fois les tags mis en place, on a un nouveau moyen de décider quels tests on souhaite lancer ou non. Nous verrons des exemples d’usages dans la suite de l’article.&lt;/p&gt;
&lt;h3 id=&quot;mon-lanceur-de-tests-ne-g%C3%A8re-pas-les-tags&quot; tabindex=&quot;-1&quot;&gt;Mon lanceur de tests ne gère pas les tags&lt;/h3&gt;
&lt;p&gt;C’est pas de bol...&lt;/p&gt;
&lt;p&gt;Mais il y a de fortes chances que la communauté propose un plug-in qui permet d’avoir un mécanisme similaire&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Si ce n’est pas le cas tout n’est pas perdu. Il y a de grandes chances que le lanceur de tests permette de filtrer les tests en fonction de leur nom&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. &lt;strong&gt;On peut alors décider d’inclure les tags à la fin du nom des tests et filtrer sur le pattern qui nous intéresse.&lt;/strong&gt; Ajouter un tag dans le nom du test rajoute du bruit, de l’information qui n’est pas pertinente dans certains cas, et il est important d’avoir une convention qui permet de facilement savoir si une information est un tag ou non. Commencer l’ensemble des tags par un croisillon (#) est sans doute une bonne idée.&lt;/p&gt;
&lt;h2 id=&quot;examples-d%E2%80%99usage&quot; tabindex=&quot;-1&quot;&gt;Examples d’usage&lt;/h2&gt;
&lt;p&gt;Voyons quelques idées de ce que nous permettent de faire les tags.&lt;/p&gt;
&lt;h3 id=&quot;type-de-test&quot; tabindex=&quot;-1&quot;&gt;Type de test&lt;/h3&gt;
&lt;p&gt;La première idée est la plus évidente, si évidente qu’elle peut sembler inutile, et pourtant...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Les tags peuvent permettre d’indiquer le type du test.&lt;/strong&gt; S’agit-il d’un test unitaire, d’un test d’intégration, d’un test end-2-end, d’un test d’acceptance, ou d’un autre type de test ?&lt;/p&gt;
&lt;p&gt;Avec les tags, on peut alors décider de ne lancer que certains types de tests à certains moments.&lt;/p&gt;
&lt;p&gt;Cette idée semble inutile puisqu’il y a des chances que vous vous serviez déjà de test suites rangées dans des dossiers différents pour séparer les types de tests, et qu’il vous est déjà possible de décider quels types de test exécuter.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;L’inconvénient du tri des types de tests par dossier est qu’elle nous force à maintenir synchronisé une ou plusieurs hiérarchies de fichiers miroirs de celle du code de production.&lt;/strong&gt; Et avouons-le franchement, la plupart du temps cette synchronisation n’est pas faite. On a alors des hiérarchies de dossiers complètement différentes de tous les côtés.&lt;/p&gt;
&lt;p&gt;Grâce aux tags, on peut abandonner cette contrainte et placer tous les tests dans une même hiérarchie, ce qui nous facilite grandement la maintenabilité.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On peut même envisager d’aller plus loin et de mettre les tests directement à côté du code et quand même avoir un moyen de choisir quels types de tests on veut lancer.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Et là, plus de hiérarchies de dossier à maintenir synchronisées et pourtant il est possible de sélectionner facilement quels types de test on souhaite lancer.&lt;/p&gt;
&lt;h3 id=&quot;d%C3%A9pendances&quot; tabindex=&quot;-1&quot;&gt;Dépendances&lt;/h3&gt;
&lt;p&gt;La seconde idée est une continuation de la précédente.&lt;/p&gt;
&lt;p&gt;Mais d’abord permettez-moi de vous poser quelques questions :&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Combien de fois avez-vous eu un débat pour décider si un test est un test unitaire ?&lt;/strong&gt; Ou plutôt un test d’intégration ? Ou un alors test d’acceptance ? Peut-être plutôt un test end-2-end ?&lt;br&gt;
Combien de temps avez-vous perdu à vous écharper en équipe pour savoir comment classer un test et dans quel dossier le ranger ?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Et si le plus important ce n’est pas le type du test mais ses caractéristiques ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pensez-y 2 minutes : &lt;strong&gt;pourquoi tient-on tant à connaitre le type d’un test ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Est-ce que pouvoir séparer les tests trop lents des autres n’y est pas pour beaucoup ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Si votre manière de travailler s’appuie beaucoup sur les tests, par exemple si vous pratiquez le TDD, vous n’avez surement pas envie d’attendre un long moment avant de savoir si vos modifications ont eu un impact imprévu sur le système. Il y a des chances que vous lanciez fréquemment les tests les plus rapides, comme un premier filet de sécurité, et de temps à autre l’ensemble de tous les tests, même les plus lents, pour avoir un filet de sécurité plus complet.&lt;/p&gt;
&lt;p&gt;Alors plutôt que de débattre sur le type des tests demandons-nous pourquoi ils sont lents. Généralement les tests sont lents dès lors que l’on fait des entrées/sorties. Les tests qui touchent à une base de données, à un système de fichier, qui font des appels à des services externes via le réseau sont bien plus lents que les autres tests&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;On tient alors notre nouvelle idée de catégorie de tags: des tags qui indiquent les dépendances.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lorsqu’un test va avoir besoin d’une dépendance particulière on peut l’indiquer avec un tag, comme &lt;code&gt;database&lt;/code&gt;, &lt;code&gt;external-call&lt;/code&gt;, &lt;code&gt;network&lt;/code&gt;, &lt;code&gt;filesystem&lt;/code&gt;, ...&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Avec ce type de tags &lt;strong&gt;on est plus obligé d’essayer de catégoriser les tests précisément, on a simplement à décrire les dépendances dont ils ont besoin, ce qui va nous éviter des débats sans fin.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;On peut désormais sélectionner ou exclure certains tests en fonction de ce dont ils dépendent. Lorsque l’on a besoin de ne lancer que les tests les plus rapides il est facile d’exclure les dépendances pénibles.&lt;/p&gt;
&lt;p&gt;En bonus, s’il manque une dépendance dans un environnement, par exemple si l’on n’a pas moyen de faire des appels à des services externes parce qu’on est dans le train, on peut facilement exclure tous les tests qui ne passeront de toute façon pas.&lt;/p&gt;
&lt;p&gt;Cette idée de sélection par dépendance, qui est ingérable avec le système de fichier, devient possible avec les tags.&lt;/p&gt;
&lt;h3 id=&quot;documentation-plus-pr%C3%A9cise&quot; tabindex=&quot;-1&quot;&gt;Documentation plus précise&lt;/h3&gt;
&lt;p&gt;Je parle souvent d’&lt;a href=&quot;/articles/des-tests-super-lisibles/&quot;&gt;utiliser les tests comme documentation&lt;/a&gt;, pour les devs, mais aussi pour le reste de l’équipe, voir même pour le &amp;quot;métier&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./documentation.png&quot; alt=&quot;De la documentation générée via phpunit --testdox.&quot;&gt;&lt;/p&gt;
&lt;p&gt;En PHP, avec PHPUnit, l’option &#39;--testdox&#39; permet de générer de la documentation en texte ou sous forme d’un fichier HTML, sauf que...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Différentes personnes sont intéressées par différents aspects du système. Plutôt que de générer des documents énormes que personne ne va lire il est possible d’ajouter des tags pour chaque sujet d’intérêt.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Avec ces tags on peut maintenant générer une documentation spécifique pour chacun :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;une documentation qui parle des fonctionnalités de paiements en filtrant sur le tag &lt;code&gt;feat-payment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;une documentation sur le fonctionnement du backoffice avec &lt;code&gt;tool-bo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Là encore on peut s’en sortir avec le système de fichier mais &lt;strong&gt;il existe aussi des sujets plus transverses&lt;/strong&gt;, tels que la sécurité (&lt;code&gt;security&lt;/code&gt;), la traduction (&lt;code&gt;i18n&lt;/code&gt;), ou les règles métiers qui ne concernent que les clients de certains pays (&lt;code&gt;market-es&lt;/code&gt;)...&lt;/p&gt;
&lt;p&gt;En se servant des tags on peut mettre des marqueurs sur les sujets qui nous intéressent de manière récurrente.&lt;/p&gt;
&lt;h3 id=&quot;lien-avec-le-syst%C3%A8me-de-tickets&quot; tabindex=&quot;-1&quot;&gt;Lien avec le système de tickets&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Les tags peuvent également permettre de faire le lien entre un système de gestion de tickets.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Avec le numéro du ticket en tant que tag on peut facilement retrouver un test de non-régression pour un bug &lt;code&gt;bug-3457&lt;/code&gt; ou trouver tous les tests liés à une user story &lt;code&gt;US-89&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Je ne pense pas que ces tags soient très utiles sur le long terme puisqu’après un certain temps savoir qu’un comportement a été créé en lien avec un ticket n’est plus une information très pertinente selon moi, mais ils peuvent toutefois servir à court terme.&lt;/p&gt;
&lt;p&gt;Par exemple, l’outil de build pourrait générer une documentation spécifique basée sur les tests pour chaque user story et l’afficher directement dans un commentaire de pull request&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt; ou vérifier qu’un test de non-régression a bien été posé avant de résoudre le bug.&lt;/p&gt;
&lt;h2 id=&quot;nomenclature&quot; tabindex=&quot;-1&quot;&gt;Nomenclature&lt;/h2&gt;
&lt;p&gt;Comme pour tout outil, en abuser peut avoir de désagréables conséquences.&lt;/p&gt;
&lt;p&gt;Pour éviter que l’utilisation des tags ne devienne un immense foutoir &lt;strong&gt;il est important de réfléchir à une nomenclature&lt;/strong&gt;. Pour cela il faut se demander ce que vous pouvez tirer de l’utilisation des tags, que ce soit pour sélectionner les tests à lancer ou pour extraire de l’information à partir de ceux-ci.&lt;/p&gt;
&lt;p&gt;Une fois que vous avez défini ce à quoi vont vous servir les tags et identifié des catégories &lt;strong&gt;vous pouvez utiliser des préfixes pour facilement identifier les tags&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;On a croisé quelques idées dans cet article :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;feat-payment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;US-89&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bug-3457&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tool-backoffice&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;market-es&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;domain-delivery&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Pensez également à faire du nettoyage de temps à autre et à supprimer les tags qui ne vous servent plus - particulièrement si vous utilisez les tags liés au système de gestion de tickets.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./tag-list.png&quot; alt=&quot;Lister les tags avec phpunit --list-groups&quot;&gt;&lt;/p&gt;
&lt;p&gt;Pour cela, laissez-vous aider par votre lanceur de test. S’il permet de filtrer les tests par groupe il a certainement une commande permettant de lister les tags. Par exemple, PHPUnit a une commande &lt;code&gt;--list-groups&lt;/code&gt;. Si le lanceur de test ne vous aide pas vous pourrez probablement vous en sortir à coup de &lt;code&gt;grep&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Certains tags auront des durées de vie plus ou moins longues de par leur nature, vos usages changeront sans doute également, et c’est très bien.&lt;/p&gt;
&lt;p&gt;J’espère que cet article vous a donné des idées, celles présentées ou d’autres, sur la manière dont vous pouvez incorporer l’usage des tags à votre quotidien pour tirer le meilleur parti de vos tests.&lt;/p&gt;
&lt;p&gt;Et si vous voulez faire en sorte d&#39;avoir des tests avec lesquels vous prendrez un réel plaisir à travailler, vous pouvez accéder à &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;ma formation vidéo sur l&#39;amélioration des tests automatisés&lt;/a&gt;. Il vous est aussi possible de &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;prendre un rendez-vous&lt;/a&gt; pour que nous travaillions ensemble à faire passer votre équipe au niveau supérieur.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Par exemple, &lt;a href=&quot;https://www.npmjs.com/package/jest-runner-groups&quot;&gt;jest-runner-groups&lt;/a&gt; si vous utilisez Jest. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://jestjs.io/docs/cli#--testnamepatternregex&quot;&gt;L’option testnamepatternregex de Jest&lt;/a&gt; peut aider. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Oserais-je simplement dire que ces tests sont bien plus lents que les tests unitaires ? &lt;a href=&quot;#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Si votre workflow utilise des PRs, bien sûr. &lt;a href=&quot;#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Coder ses tests sans les mains grâce aux Live Templates de PhpStorm</title>
      <link href="https://blog.charlesdesneuf.com/articles/coder-ses-tests-sans-les-mains-grace-aux-live-templates-de-phpstorm/"/>
      <updated>2022-11-17T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/coder-ses-tests-sans-les-mains-grace-aux-live-templates-de-phpstorm/</id>
      <summary>Faciliter l’écriture des tests avec les Live Templates des IDE Jetbrains</summary>
      <content type="html">&lt;p&gt;Je ne tape pas particulièrement vite, en tous clairement pas avec les 5 doigts des 2 mains&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; mais j’arrive à compenser parce que j’ai trouvé différentes manières d’écrire le même code que quelqu’un qui taperait vite tout en seulement quelques caractères.&lt;/p&gt;
&lt;p&gt;Les Live Templates des IDE Jetbrains, et donc de PhpStorm, font clairement partie de mes fonctionnalités préférées.&lt;/p&gt;
&lt;p&gt;Dans cet article je vais vous partager certains des Lives Templates que j’utilise lorsque je travaille avec des tests, ce qui devrait vous faire découvrir la puissance de cet outil.&lt;/p&gt;
&lt;p&gt;On va commencer par des Live Templates simples et on verra sur la fin ce que l’on peut faire quand on commence à pousser l’outil presque au maximum&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt; de ce qu’il est capable de faire.&lt;/p&gt;
&lt;h2 id=&quot;comment-r%C3%A9cup%C3%A9rer-ces-live-templates&quot; tabindex=&quot;-1&quot;&gt;Comment récupérer ces Live Templates&lt;/h2&gt;
&lt;p&gt;Pour chacun des exemples de cet article je vous partage le snippet XML qui le représente, ce qui va vous permettre de facilement le mettre en place dans votre IDE.&lt;/p&gt;
&lt;p&gt;Pour cela :&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Copiez le snippet XML&lt;/li&gt;
&lt;li&gt;Dans PhpStorm, ouvrez les préférences et cherchez &lt;code&gt;Live Templates&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Sélectionnez le dossier dans lequel vous voulez ajouter le Live Template. Sans doute &lt;code&gt;Php&lt;/code&gt; ou &lt;code&gt;PhpUnit&lt;/code&gt; pour ceux de cet article.&lt;/li&gt;
&lt;li&gt;Gardez la sélection sur le dossier et collez, avec &lt;code&gt;CTRL&lt;/code&gt; + &lt;code&gt;V&lt;/code&gt; ou &lt;code&gt;CMD&lt;/code&gt; + &lt;code&gt;V&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Adaptez-le si besoin !&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;cr%C3%A9ation-de-cas-de-test&quot; tabindex=&quot;-1&quot;&gt;Création de cas de test&lt;/h2&gt;
&lt;p&gt;J’utilise plusieurs Live Templates qui me permettent de créer facilement des cas de tests.&lt;/p&gt;
&lt;h3 id=&quot;cr%C3%A9er-un-cas-de-test-et-commencer-%C3%A0-tester&quot; tabindex=&quot;-1&quot;&gt;Créer un cas de test et commencer à tester&lt;/h3&gt;
&lt;div class=&quot;img-full&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./t.gif&quot; alt=&quot;Un gif qui fait la démonstration du live template: &amp;quot;@t&amp;quot;, tab, écrire le nom du test, tab, écrire le test&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Le premier, &lt;code&gt;@t&lt;/code&gt;, pour &amp;quot;test&amp;quot;, est celui que j’utilise le plus souvent. Il aide à créer un test avec l’annotation &lt;code&gt;@test&lt;/code&gt;, plutôt qu’avec le prefix &lt;code&gt;test&lt;/code&gt;. Cela permet d’avoir des noms de &lt;a href=&quot;/articles/des-tests-super-lisibles/&quot;&gt;tests plus lisibles&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@t&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/**&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;* @test&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;*/&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;public function $NAME$(): void {&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;    $END$&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Add a test function&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toReformat&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toShortenFQNames&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;NAME&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PHP Class Member&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;cr%C3%A9er-un-cas-de-test-pour-plus-tard&quot; tabindex=&quot;-1&quot;&gt;Créer un cas de test pour plus tard&lt;/h3&gt;
&lt;p&gt;Quand on fait du TDD on a parfois envie de lister des cas de tests en avance. Il y a plusieurs options: noter sur une feuille de papier, mettre des commentaires, ou créer des méthodes tests.&lt;/p&gt;
&lt;p&gt;Pour faciliter la création de méthodes de test &amp;quot;pense-bête&amp;quot; j’ai un autre Live Template, &lt;code&gt;@ts&lt;/code&gt;, pour &amp;quot;test skipped&amp;quot;.&lt;/p&gt;
&lt;div class=&quot;img-full&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./ts.gif&quot; alt=&quot;Un gif qui fait la démonstration du live template: &amp;quot;@ts&amp;quot;, tab, ajoute un skipped test, tab, écrire le nom du test, tab, le curseur se place une ligne en dessous de la nouvelle méthode de test.&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Notez qu’une fois le nom du test écrit, le curseur se place une ligne sous la nouvelle méthode de test pour permettre d’enchainer la prise de note des idées.&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@ts&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/**&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;* @test&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;*/&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;public function $NAME$(): void {&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#9;&quot;&gt;&amp;amp;#9;&lt;/span&gt;$this-&lt;span class=&quot;token entity named-entity&quot; title=&quot;&amp;gt;&quot;&gt;&amp;amp;gt;&lt;/span&gt;markTestSkipped(&#39;Not implemented yet.&#39;);&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;}&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt; $END$&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Add a test function&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toReformat&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toShortenFQNames&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;NAME&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PHP Class Member&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Une grosse partie de ce qui fait la réussite d’un Live Template pour gagner du temps est l’endroit où se place le curseur une fois la modification terminée.&lt;/strong&gt; Quand vous créer un Live Template vous pouvez spécifier la dernière position du curseur avec la variable &lt;code&gt;$END$&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;lazy-naming-des-tests&quot; tabindex=&quot;-1&quot;&gt;Lazy Naming des tests&lt;/h3&gt;
&lt;p&gt;Un dernier exemple de création de méthode de test.&lt;/p&gt;
&lt;p&gt;J’ai envie d’expérimenter avec l’idée de Lazy Naming&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;. En Lazy Naming on ne va nommer les tests qu’une fois qu’ils sont dans leur forme finale, ou que l’on en est assez satisfait pour daigner faire l’effort de les nommer.&lt;/p&gt;
&lt;p&gt;Le Live Template &lt;code&gt;@tl&lt;/code&gt;, pour &amp;quot;test lazy&amp;quot;, rajoute un nouveau test dont le nom est généré en utilisant le numéro de la ligne et le curseur est directement placé dans le corps de la méthode.&lt;/p&gt;
&lt;div class=&quot;img-full&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./tl.gif&quot; alt=&quot;Un gif qui fait la démonstration du live template: &amp;quot;@tl&amp;quot;, tab, une méthode de test avec un nom généré est ajoutée, et on peut écrire le test&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;@tl&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/**&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;* @test&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;*/&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;public function TODO_RENAME_$LINE_NUMBER$(): void {&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;    $END$&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Add a lazy named test&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toReformat&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toShortenFQNames&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;LINE_NUMBER&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;lineNumber()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PHP Class Member&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;builders&quot; tabindex=&quot;-1&quot;&gt;Builders&lt;/h2&gt;
&lt;p&gt;Passons maintenant aux choses un peu plus sérieuses !&lt;/p&gt;
&lt;p&gt;J’utilise énormément de builders dans les tests. &lt;a href=&quot;/articles/des-tests-super-lisibles/&quot;&gt;Ils facilitent fortement la lisibilité et la maintenabilité des tests&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Le seul problème des builders c’est leur mise en place qui demande un peu d’investissement. C’est souvent la raison pour laquelle même ceux qui connaissent et aiment cette technique remettent à plus tard leur création.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Et plus tard, c’est en général trop tard. Au moment où on commence à souffrir de leur manque il faut repasser sur tout un tas de tests pour les mettre en place...&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Mais les Live Templates peuvent nous aider à mettre en place nos builders super rapidement, si rapidement que la question de créer ou ne pas créer un builder ne se pose plus.&lt;/p&gt;
&lt;p&gt;Laissez-moi vous présenter la classe &lt;code&gt;Burrito&lt;/code&gt;, qui va nous servir de support d’exemple pour la suite.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Burrito&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$burritoName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;Salsa&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$salsa&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$ingredients&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Le constructeur de cette classe à 3 paramètres :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$burritoName&lt;/code&gt; de type &lt;code&gt;string&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$salsa&lt;/code&gt; de type &lt;code&gt;Salsa&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;$ingredients&lt;/code&gt; de type &lt;code&gt;array&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;cr%C3%A9ation-d%E2%80%99une-m%C3%A9thode-de-param%C3%A9trage-du-builder&quot; tabindex=&quot;-1&quot;&gt;Création d’une méthode de paramétrage du builder&lt;/h3&gt;
&lt;p&gt;Dans la classe &lt;code&gt;BurritoBuilder&lt;/code&gt; il faut ajouter des méthodes qui vont nous permettre de paramétrer la construction du &lt;code&gt;Burrito&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;img-full&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./with.gif&quot; alt=&quot;Un gif qui fait la démonstration du live template &amp;quot;with&amp;quot;. La description est ci-dessous.&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Dans l’exemple on ajoute deux méthodes de paramétrage en quelques secondes. L’une pour la sauce et l’autre pour le nom du burrito. Ce Live Template fonctionne aussi bien pour un type natif que pour une classe que l’on a créée.&lt;/p&gt;
&lt;p&gt;Voilà ce qui se passe :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Je tape &amp;quot;with&amp;quot;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Tabulation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;J’indique le type du paramètre. &lt;code&gt;Salsa&lt;/code&gt; ou &lt;code&gt;string&lt;/code&gt; ici.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Tabulation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Je dois indiquer le nom du paramètre. Ici l’autocomplete m’aide. S’il s’agit d’une classe il propose directement le nom de la classe comme nom de variable. &lt;code&gt;$salsa&lt;/code&gt; par exemple.&lt;/li&gt;
&lt;li&gt;Le nom me convient, &lt;code&gt;Entrée&lt;/code&gt;. Le nom me convient pas, je le change.&lt;/li&gt;
&lt;li&gt;Le nom de la propriété à laquelle est assignée la variable prend directement le nom que je viens de choisir&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Tabulation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Le curseur se place au niveau de l’assignation de la propriété&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Alt&lt;/code&gt; + &lt;code&gt;Entrée&lt;/code&gt;, le raccourci à tout faire de l’IDE.&lt;/li&gt;
&lt;li&gt;Choisir d’ajouter la propriété manquante&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Entrée&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Et voilà. La propriété est créée, la méthode pour lui assigner une valeur également.&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;with&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;public function with$NAME$($PARAMETER_TYPE$ $$$PARAMETER_NAME$): self&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;{&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#9;&quot;&gt;&amp;amp;#9;&lt;/span&gt;$clone = clone $this;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#9;&quot;&gt;&amp;amp;#9;&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#9;&quot;&gt;&amp;amp;#9;&lt;/span&gt;$clone-&lt;span class=&quot;token entity named-entity&quot; title=&quot;&amp;gt;&quot;&gt;&amp;amp;gt;&lt;/span&gt;$PARAMETER_NAME$$END$ = $$$PARAMETER_NAME$;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#9;&quot;&gt;&amp;amp;#9;&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#9;&quot;&gt;&amp;amp;#9;&lt;/span&gt;return $clone;&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Create a wither&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toReformat&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toShortenFQNames&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PARAMETER_TYPE&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PARAMETER_NAME&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;complete()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;NAME&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;capitalize(PARAMETER_NAME)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PHP Class Member&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mais ce n’est pas fini.&lt;/p&gt;
&lt;h3 id=&quot;cr%C3%A9ation-de-la-m%C3%A9thode-build&quot; tabindex=&quot;-1&quot;&gt;Création de la méthode &lt;code&gt;build&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;Qui dit builder dit méthode &lt;code&gt;build&lt;/code&gt;. Toutes les méthodes &lt;code&gt;build&lt;/code&gt; se ressemblent. Au moins au début.&lt;/p&gt;
&lt;p&gt;On construit un objet en lui passant en paramètre les propriétés du builder et on le retourne.&lt;/p&gt;
&lt;p&gt;Il est également possible d’utiliser un Live Template pour là aussi gagner du temps. En réalité il s’agit même de deux Live Templates qui se combinent bien ensemble.&lt;/p&gt;
&lt;div class=&quot;img-full&quot;&gt;
&lt;p&gt;&lt;img src=&quot;./build.gif&quot; alt=&quot;Un gif qui fait la démonstration du live template &amp;quot;with&amp;quot;. La description est ci-dessous.&quot;&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Le premier Live Template, &lt;code&gt;build&lt;/code&gt;, créer la méthode build, place le curseur au niveau du type de retour, l’autocomplétion est lancée et nous aide à choisir le type.&lt;/p&gt;
&lt;p&gt;Automatiquement le constructeur du type est appelé avec le mot clé &lt;code&gt;new&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On fait une tabulation et le curseur se place dans la liste des paramètres. L’autocomplétion est démarrée et nous propose la bonne propriété à utiliser pour le premier paramètre.&lt;/p&gt;
&lt;p&gt;C’est là que le second LT entre jeu. Le constructeur de &lt;code&gt;Burrito&lt;/code&gt; prend plusieurs paramètres et il est possible de les ajouter très rapidement.&lt;/p&gt;
&lt;p&gt;Ce second Live Template est bindé sur &lt;code&gt;,&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Oui, oui, la virgule.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;,&lt;/code&gt; + &lt;code&gt;Tabulation&lt;/code&gt; démarre automatiquement l’autocomplétion, qui se charge de proposer la bonne propriété pour le paramètre.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Le snippet du Live Template &lt;code&gt;build&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;build&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;public function build(): $TYPE$&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;{&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;    return new $TYPE$($PARAMETERS$$END$);&lt;span class=&quot;token entity&quot; title=&quot;&amp;#10;&quot;&gt;&amp;amp;#10;&lt;/span&gt;}&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Build method&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toReformat&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toShortenFQNames&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;TYPE&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;complete()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PARAMETERS&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;completeSmart()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PHP Class Member&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;et celui de &lt;code&gt;,&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;,&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;, $COMPLETE$&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;, and auto complete&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toReformat&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toShortenFQNames&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;COMPLETE&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;completeSmart()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PHP Expression&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PHP Statement&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;J’espère que ces quelques exemples d’utilisation des Live Templates vous auront donné envie de créer les vôtres. Si vous cherchez de l’inspiration je vous invite à lire &lt;a href=&quot;https://marijn.huizendveld.com/blog/live-templates-in-phpstorm&quot;&gt;cet article de Marijn Huizendveld&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt; dans lequel il partage ces Live Templates pour créer des Command Handlers, des Values Objects, des collections...&lt;/p&gt;
&lt;p&gt;Devenez fainéants !&lt;/p&gt;
&lt;p&gt;Et si vous pensez qu&#39;un accompagnement technique pourrait être bénéfique pour votre équipe, &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;rencontrons-nous&lt;/a&gt; et trouvons ensemble comment vous faire passer au niveau supérieur.&lt;/p&gt;
&lt;p&gt;Si vous voulez prêcher la bonne parole de la fainéantise auprès de vos amis cet article est aussi disponible sous forme de thread Twitter&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;div style=&quot;margin: auto; width: 75%&quot;&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;fr&quot; dir=&quot;ltr&quot;&gt;Les live templates sont une de mes features préférées de &lt;a href=&quot;https://twitter.com/phpstorm?ref_src=twsrc%5Etfw&quot;&gt;@phpstorm&lt;/a&gt;.&lt;br&gt;&lt;br&gt;Ça me fait gagner énormémement de temps. Faut dire que je tape pas bien vite.&lt;br&gt;&lt;br&gt;Comme je parle beaucoup de tests, petit thread d&amp;#39;illustration des live templates qui me servent quand j&amp;#39;écris des tests ! 👇 &lt;a href=&quot;https://t.co/VeZkjJGcg3&quot;&gt;pic.twitter.com/VeZkjJGcg3&lt;/a&gt;&lt;/p&gt;&amp;mdash; Charles Desneuf - @selrahcd@piaille.fr (@Selrahcd) &lt;a href=&quot;https://twitter.com/Selrahcd/status/1593197582892683266?ref_src=twsrc%5Etfw&quot;&gt;November 17, 2022&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt; &lt;/div&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Ça doit avoisiner 4 doigts sur les deux mains. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Il est &lt;a href=&quot;https://twitter.com/pronskiy/status/1592520783976599552&quot;&gt;possible de faire tourner des scripts externes&lt;/a&gt;, mais je n’ai pas encore eu l’occasion de tester. J’ai quelques idées cela dit. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Je tiens cette idée de Lazy Naming de &lt;a href=&quot;https://twitter.com/malk_zameth&quot;&gt;Romeu Moura&lt;/a&gt; qu’il présente dans &lt;a href=&quot;https://www.youtube.com/watch?v=RlkgetzDenI&quot;&gt;cette conférence&lt;/a&gt; &lt;a href=&quot;#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Le Live Template bindé sur la virgule est une nouvelle idée et est en cours d’expérimentation. Je ne serais pas étonné d’avoir des mauvaises surprises un de ces jours 😅. Vous êtes prévenus. &lt;a href=&quot;#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;C’est cet article qui m’a fait connaitre la méthode facile de partage des Live Templates. Merci Marijn. &lt;a href=&quot;#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Tant que Twitter existe, tout du moins. &lt;a href=&quot;#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Facilitation Nightmares: How to escape a workshop not going on as planned</title>
      <link href="https://blog.charlesdesneuf.com/articles/facilitation-nightmares-how-to-escape-a-workshop-not-going-on-as-planned/"/>
      <updated>2023-01-11T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/facilitation-nightmares-how-to-escape-a-workshop-not-going-on-as-planned/</id>
      <summary>As facilitators, what should we do when our workshop are derailing ?</summary>
      <content type="html">&lt;p&gt;A few days ago, I hosted a session about Facilitation Nightmares during a &lt;a href=&quot;https://virtualddd.com/&quot;&gt;Virtual DDD&lt;/a&gt; gathering organised as an open forum.&lt;/p&gt;
&lt;p&gt;The idea was for the participants to share some of their stories of facilitation going astray and to explain how they remedied the situation or to see if other participants would have propositions on what they could have done.&lt;/p&gt;
&lt;p&gt;As a group, we discussed different situations. Here are the three I enjoyed the most and wanted to keep with me because they all had unusual solutions.&lt;/p&gt;
&lt;p&gt;Let’s start with you, the facilitator, being excluded.&lt;/p&gt;
&lt;h2 id=&quot;the-%22you-know-nothing%2C-john-snow%22-situation&quot; tabindex=&quot;-1&quot;&gt;The &amp;quot;You know nothing, John Snow&amp;quot; Situation&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/dianamontalion&quot;&gt;Diana Montalion&lt;/a&gt; described the &amp;quot;You know nothing, John Snow&amp;quot; situation. She explained that during some engagements, she was facilitating architectural sessions. I don’t remember if she mentioned what they were doing, but I can imagine it was an activity we love in the DDD community but can feel strange for other people, such as Event Storming. Unfortunately, some people in the room wanted to avoid engaging in the workshop, claiming it wasn’t architecture. Who was she to make them lose their time that way?&lt;/p&gt;
&lt;p&gt;Diana shared that the feeling she had was that they wanted to do things their way, as they usually did, and that the message they had was that she didn’t have anything to bring to the table. Later in the discussion, we rephrased the situation as the &amp;quot;You don’t belong here&amp;quot; situation.&lt;/p&gt;
&lt;p&gt;Together, with the group, we discussed some ideas on how to avoid that situation.&lt;/p&gt;
&lt;p&gt;We pointed out the usual stuff about facilitation. At the beginning of the workshop, share why we are running it and make sure everyone agrees on the goals. Make it really obvious and easy to see, so you can get back to them during the session if needed. You can use a &amp;quot;Purpose slide&amp;quot; or write them on a paper board.&lt;/p&gt;
&lt;p&gt;Also, establish the goal of the meeting beforehand. Let people know before they agree to come what we will discuss. It would be best if you made the invitation explicit, that way, only interested people will show up.&lt;/p&gt;
&lt;p&gt;If people find the meeting is not for them after all, allow them to leave. Explain the Law Of Two Feet at the start of the workshop: if you’re neither learning nor contributing, you can use your two feet to move to something more interesting to you.&lt;/p&gt;
&lt;p&gt;Another idea is to let people know you at the beginning or even before the session. If people know who you are and what you’ve done, it adds to your credibility. Show that you have the right to be here and some experiences to share. Someone introducing Diana as a long-time architect and frequent speaker could have mitigated the situation.&lt;/p&gt;
&lt;p&gt;Here you are using your rank, demonstrating that you are a player in the game and know things to mitigate the situation.&lt;/p&gt;
&lt;p&gt;Doing your own introduction can be a bit awkward, and having allies before the meeting can significantly help. Find some people who know you and your past work and who will make the introduction. If needed, they will remind hard-to-manage participants that you have something to bring to the discussion too.&lt;/p&gt;
&lt;p&gt;While Diana faced people being vocal about not wanting to participate in the workshop, sometimes, as facilitators, we met more quiet people, even non-talkative ones.&lt;/p&gt;
&lt;h2 id=&quot;people-not-willing-to-speak&quot; tabindex=&quot;-1&quot;&gt;People not willing to speak&lt;/h2&gt;
&lt;p&gt;How can we cope with people remaining silent during the workshop?&lt;/p&gt;
&lt;p&gt;First, as facilitators, we must remember that some people are quieter than others. Not all of us are comfortable with the potential mess of the workshops.&lt;/p&gt;
&lt;p&gt;One proposed solution is to make yourself available after the workshop. Sometimes people can’t speak during the session: it’s not a good time for them, and the environment is inappropriate.&lt;/p&gt;
&lt;p&gt;You have multiple solutions to make yourself available. The obvious one is to share some way to contact you afterwards, like your email address.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://twitter.com/ziobrando&quot;&gt;Alberto Brandolini&lt;/a&gt; proposed an idea that resonated with me:  never leave the room on time. Hang around in the room after the end of the workshop. Take your time to tidy the room. Some people might stay and help you. As people are living, you might have the chance to hear some voices you haven’t heard until now.&lt;/p&gt;
&lt;p&gt;Alberto shared a story of one participant who didn’t talk during a challenging workshop, staying silent in the corner of the room. Once the workshop ended and the other participants had left, he went to Alberto and explained what was happening during the workshop. He described the company situation, the power games, and the toxic relationships between participants. Suddenly, everything happening during the session became clearer.&lt;/p&gt;
&lt;p&gt;Another idea we didn’t discuss that day, but I’m pretty sure I’ve heard Alberto talk about before,  is to have a drink with participants after the workshop. Some people might feel more comfortable in a different environment and start to speak. You might get extra information helping to do your job.&lt;/p&gt;
&lt;p&gt;An example of the environment not being as safe as it should be for the workshop to go smoothly is when some participants have more power than others. For instance, when the bosses participate.&lt;/p&gt;
&lt;h2 id=&quot;the-boss-derailing-the-workshop&quot; tabindex=&quot;-1&quot;&gt;The boss derailing the workshop&lt;/h2&gt;
&lt;p&gt;Having the bosses around can make the discussions more difficult than it should be. People might stop talking, look at the boss for approval for everything they say or worst, you could encounter a boss who decides to &amp;quot;show them how things are done&amp;quot; because &amp;quot;he knows better&amp;quot; and &amp;quot;it will be faster if he explains it&amp;quot;. In that case, you lose the collaborative part of the workshop and get only one point of view. And more, you get the point of view of someone who is probably not doing the actual job and has some blind spots.&lt;/p&gt;
&lt;p&gt;While having the boss in the room can give you a different perspective, it’s not always an ideal situation.&lt;/p&gt;
&lt;p&gt;To avoid the boss capturing the mic and not letting other people speak, a solution is to round-robin the timer. Everyone can talk for some amount of time, and once the time is over, we’re moving to the next person. This idea obviously works with anyone, big chief or not, because, let’s be honest, bosses don’t have the monopoly of talking too much.&lt;/p&gt;
&lt;p&gt;Another excellent idea proposed by &lt;a href=&quot;https://mastodon.social/@weltraumpirat&quot;&gt;Tobias Goeschel&lt;/a&gt; is to occupy the boss. This is easier said than done because you must be at least two facilitators. One remains working with the group, and the other creates a new activity in another part of the room. For this, you can use a paper board and ask him to explain his ideas and vision of the world. As long as you make him feel important, you’ll be able to keep working with the rest of the group.&lt;/p&gt;
&lt;p&gt;Alberto reminded us there is no such thing as a boss showing up late to a workshop to be a spectator. At some point, they will want to speak, and change things the group has already discussed. Even if they remain silent, just by being there, they will change the environment, and the room’s dynamic might be different: some people will talk more, others will speak less, discussions will move to another topic, ...&lt;/p&gt;
&lt;p&gt;In this blog post, I focused on the three situations we talked about that night I most relate to. We had other exciting discussions, and we only covered some of the ideas we wanted to discuss. Have a look at &lt;a href=&quot;https://miro.com/app/board/uXjVPEdHYFg=/?moveToWidget=3458764542322553694&amp;amp;cot=14&quot;&gt;the board&lt;/a&gt; and see if some of the situations and remediation ideas are interesting to you for your facilitation practice.&lt;/p&gt;
&lt;p&gt;I think it would be valuable to gather more facilitation stories. First, they can be fun to hear, and also, because we can learn from them. If you like that idea and have stories to share, hit me up, and let’s see what we can do!&lt;/p&gt;
&lt;p&gt;And maybe you are looking for a facilitator, &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;that would be a fanstatic opportunity to meet&lt;/a&gt; and see what we can do together!&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Approval testing: 3 technics to deal with randomness</title>
      <link href="https://blog.charlesdesneuf.com/articles/approval-testing-3-technics-to-deal-with-randomness/"/>
      <updated>2023-01-17T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/approval-testing-3-technics-to-deal-with-randomness/</id>
      <summary>What can we do to build a safety net on top of a system with random result ? Here are 3 technics to help.</summary>
      <content type="html">&lt;p&gt;&lt;em&gt;Cet article est disponible &lt;a href=&quot;/articles/3-techniques-pour-gerer-laleatoire-en-approval-testing/&quot;&gt;en français&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;When dealing with legacy code, randomness is not something you enjoy meeting.&lt;/p&gt;
&lt;p&gt;Creating a test harness on top of a system with random results can be pretty challenging.&lt;/p&gt;
&lt;p&gt;Obviously, you’d like your tests to be deterministic and to always give you the same result, provided you didn’t change anything in the system.&lt;/p&gt;
&lt;p&gt;Let’s see three ideas that can help you deal with the randomness in the system. The first two technics are based on the idea of removing the random part of the system during tests.&lt;/p&gt;
&lt;h2 id=&quot;block-the-random-generator&quot; tabindex=&quot;-1&quot;&gt;Block the random generator&lt;/h2&gt;
&lt;p&gt;The first solution I wanted to offer in this article is to ensure that the random generator always returns the same results when called. The answer here is to seed the generator with a value you control, to make the generator work in a deterministic manner.&lt;/p&gt;
&lt;p&gt;For instance, in PHP, you can call the &lt;code&gt;mt_rand&lt;/code&gt; function. Later, any call to the &lt;code&gt;rand&lt;/code&gt; function will return the same random numbers in the same order.&lt;/p&gt;
&lt;p&gt;This is a fast way to turn the non-deterministic system into something that always gives the same results.&lt;/p&gt;
&lt;p&gt;Unfortunately, this solution, while being one of the quickest ways to get the job done, only works in some situations. First, not all languages allow seeding the random number generator. If you can’t, you can’t and need another solution.&lt;/p&gt;
&lt;p&gt;Another caveat of this solution is that if you modify the structure of your code and the order of calls to a random method is changed; you’ll get results in the same order but not in the same logical space. For instance, let’s take two calls to &lt;code&gt;rand&lt;/code&gt; and assign the result of the first call to variable A and the second call to variable B. After seeding the random number generator, A and B are always set to the same value, say 34 and 1879. During a refactoring, if we decide to invert the order assignation, we have a problem. The code now assigns values to B before A; A is now 1879, and B is 39. You can play with this idea &lt;a href=&quot;https://onlinephp.io?s=bZDBTsMwDIbPVMo7mKrSutNo6WAFKjTQJA67wXGXrHXXSmlSOenGhDjyFjwdT0KaFiYkpBxi-_f_2b67b6uWecwrO5mbWkkgLgvVrHHH8-OjKjCcwhvzzmYzeKEj1HKPZGq5A1MhmIOCUgmhDn1G1BK1lQYcMmcTTm_7cPsntI_QdCTBX0KtwaptDR7cf-tbyXs_D_NGYq6aBuWItJqewjztDKPe0SmfkLB3UB0Bb1tSey7AoDbMC_C1xdxgYceYOOQinc-T6zSOT-Q4SqLkcnFxlU6sZcBz01mD7J9rOGJdhj-a8wx-CcOpMK8U-CsiRRu5Gks3J9VGLl2rTQ0e49IoNMLQ_PX58cwbBELdCeODg34D&amp;amp;v=8.2.1&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;What can we do when seeding the random number generator is impossible, or we might want to change the order of the calls at some point?&lt;/p&gt;
&lt;h2 id=&quot;control-the-randomness&quot; tabindex=&quot;-1&quot;&gt;Control the randomness&lt;/h2&gt;
&lt;p&gt;The other alternative is to take back control of the values generation. As often, adding a layer of abstraction is one of the solutions available in our toolbox. Here we want to break the direct dependency to the random value generator and swap it with a dependency we can manipulate.&lt;/p&gt;
&lt;p&gt;In more concrete terms, instead of directly calling a function from the language, we encapsulate it, create a stub we can manipulate, and find a way to substitute the original implementation with our double.&lt;/p&gt;
&lt;p&gt;The substitution technics available greatly vary depending on the programming language you’re using and the code’s state. If you’re lucky enough to be able to inject dependencies at construction, this is an easy task. If you’re not, some patterns from &lt;em&gt;Working Effectively With Legacy Code&lt;/em&gt; by Michael Feathers can prove helpful.&lt;/p&gt;
&lt;p&gt;This solution is available in every language. The only requirement is to feel comfortable messing around with code while not having tests to be able to take control back.&lt;/p&gt;
&lt;p&gt;Once you have control, you can decide the values generated.&lt;/p&gt;
&lt;p&gt;That solution allows us to solve the ordering issue mentioned before. If you invert the order of calls in the code, you can change the configuration of your stub to change the order and keep the tests passing.&lt;/p&gt;
&lt;p&gt;Modifying the stub after tests are created needs to be done carefully. If you want greater confidence and want to avoid touching the tests, you have another alternative. Instead of making one abstraction for every call to the random generator, you can create one for each call. Taking back the example from the first part, you can introduce an abstraction for generating A and an abstraction for generating B. Having two implementations gives you more granular control, and you can create two stubs. When you invert the call order, the code still calls the right abstractions and the same stubs and obtains the same final result.&lt;/p&gt;
&lt;p&gt;Touching an existing code base to introduce indirections to control randomly generated value is not always straightforward nor necessary. Sometimes we don’t care about the generated value, and we can be totally fine with keeping the randomness as long as we manage to keep our tests deterministic. And this is the point of the third idea.&lt;/p&gt;
&lt;h2 id=&quot;hide-the-randomness&quot; tabindex=&quot;-1&quot;&gt;Hide the randomness&lt;/h2&gt;
&lt;p&gt;Sometimes the random value is not really important for the result. If we don’t care about it, we can remove it from what is asserted in the test.&lt;/p&gt;
&lt;p&gt;The trick here is that you don’t need to make assertions based on the direct outputs of the system under test. Instead, you can get them and transform them to keep only the information you need to be confident enough that you’re not breaking anything.&lt;/p&gt;
&lt;p&gt;When doing approval testing, it is common to use a printer, a function or a class that will transform the outputs to make them look easy to understand. If a piece of information is not relevant, the printer’s role is to remove it and create a clean output. You can then use that clean output in the assertion.&lt;/p&gt;
&lt;p&gt;What could be simpler than removing what you don’t care about?&lt;/p&gt;
&lt;p&gt;Sometimes you don’t care about the actual value, but you want to ensure that a value is here. Instead of removing the random part, you can replace it with something else. Say you don’t care about the value of a UUID but want to be sure that it is displayed. Using a regular expression, you can replace all UUIDs with something else, like a &lt;code&gt;UUID&lt;/code&gt; string.&lt;/p&gt;
&lt;p&gt;Another common use case is to detect that a value is repeated in several places in the output.&lt;/p&gt;
&lt;p&gt;Let’s continue with the previous example. You still don’t care about the value of the UUID, but you want to assert that the UUID used in a link to access a product is the product’s UUID, not some other one.&lt;/p&gt;
&lt;p&gt;Here the printer has to be more clever.&lt;/p&gt;
&lt;p&gt;When a UUID is detected using a regex, the scrubber generates a new string, usually made from a base string and an increment (&lt;code&gt;UUID_1&lt;/code&gt;). Then it replaces all the subsequent occurrences of that UUID with the generated string, increments the counter and repeats for each UUID found in the output. That way, even if you can’t predetermine the result of the system, you can turn it into something deterministic that you can use as your golden master.&lt;/p&gt;
&lt;p&gt;The good thing about this solution is that it doesn’t require touching the legacy system. You don’t need to worry about breaking something while creating the tests. This is why it is a very convenient and relatively rapid method to build confidence. Also, you might be very lucky and work with a language where approval testing tools come with scrubbers doing part of the cleaning work for you.&lt;/p&gt;
&lt;p&gt;Discovering a system using randomness and needing to create a test harness doesn’t have to be scary or take an awful lot of time. With the three ideas exposed in this article, you should be able to attack that piece of legacy code you avoided refactoring for a while.&lt;/p&gt;
&lt;p&gt;If you feel like you could use a hand to gain control over a legacy codebase, &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let&#39;s chat&lt;/a&gt; and see how I can help.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>3 hacks for a magical code kata in PHP</title>
      <link href="https://blog.charlesdesneuf.com/articles/3-hacks-for-a-magical-code-kata-in-php/"/>
      <updated>2023-02-07T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/3-hacks-for-a-magical-code-kata-in-php/</id>
      <summary>How can we hide code, make test report they take minutes when they actually run in seconds and be share fake libraries when creating a code kata in PHP? I’ll show you how!</summary>
      <content type="html">&lt;p&gt;When creating content for people to learn from, having a story as a backbone can be a good idea. In my improving tests class, I’ve constructed most chapters with a story. To make a story more powerful, sometimes you need to add some magic. In this article, &lt;strong&gt;I’ll show you how I’ve created dishonest code to make a production-like environment with painful library upgrades, slow tests that aren’t really that slow, and lying code.&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Use these ideas for more common use cases&lt;/p&gt;
&lt;p&gt;The techniques I share here are useful for storytelling purposes but are, more than anything, ideas you can apply with legacy code or code organization. For each technic, I share more conventional usages.&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;some-background&quot; tabindex=&quot;-1&quot;&gt;Some background&lt;/h2&gt;
&lt;p&gt;&lt;em&gt;This article is quite long, feel free to skip if you’re only interested in the 3 hacks!&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;First, let me describe a typical chapter in &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises?utm_medium=referral&amp;amp;utm_source=blog&amp;amp;utm_campaign=3_hacks_article&quot;&gt;my testing class&lt;/a&gt;. In the beginning, we have some tests. They’re not always terribly bad, but they have room for improvement. They serve as a demonstration of a problem we can see in a lot of test suites. Usually, the next part of the story is about making a change, either in the tests or in the code, and seeing how these tests are getting in the way of that change. Then, we introduce an idea and demonstrate how to change our tests to remove that roadblock. It’s time to try again to make the change or make another related change and see how easier it is now. That story form should show students what these ideas can help them with when they struggle with their tests.&lt;/p&gt;
&lt;p&gt;One chapter of the course is about the &amp;quot;Don’t mock what you don’t own&amp;quot; advice. I’ve used the same story for years, and I like it. When I created the course, I chose to use a Burrito restaurant as the backstory for all examples, and I was lucky enough to reuse my favourite story one more time.&lt;/p&gt;
&lt;p&gt;That story is about cooking something using an oven. In that case, we’re cooking some peppers and toasting some bread. AKEI, a well-known Swedish company, manufactured our oven, a Kulinarisk. We directly use the Kulinarisk in our own code. At some point, a new version of the Kulinarisk oven is published and, obviously, breaks our code because AKEI finally decided that publishing code in Swedish was a disservice to its customers. In the V2 of the Kulinarisk library, method names are in English, which causes a BC break.&lt;/p&gt;
&lt;p&gt;I like this example because it shows a few different things:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;When you use external code, you must conform to its vocabulary.&lt;/strong&gt; In the test class, we have some code in French mixed with some Swedish words. Creating an abstraction on top of the external code gives you the power to select what terms you want to use in your code. You are free to make the vocabulary used in the code match the concept you are manipulating. No more &amp;quot;laga&amp;quot; when you’re supposed to talk about &amp;quot;baking&amp;quot;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Using the library code in many places in the code and the tests shows how painful it can be when a library makes a BC break.&lt;/strong&gt; We need to make multiple changes in our code and our tests. Touching at the tests is clearly not the best way to make a refactoring with confidence.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I’ve recently decided to create a kata version of this chapter. From what you’ve read, you already know that I needed to have at least two versions of the Kulinarisk library. I also want the learners to discuss the need to use a double instead of the real Kulinarisk. As often, an excellent reason for using a double in place of the real thing is that the real thing is slow. In my story, baking peppers to get nice and soft peppers takes 25 minutes. Clearly, that’s a bit too long to use in a kata. You don’t want people to stare at their screen for 25 minutes while waiting for the test runner to tell them if the test passed or not.&lt;/p&gt;
&lt;p&gt;Could we have our tests be slower when we’re using the real Kulinarisk, to demonstrate that using a double is a good idea, but not to slow to avoid wasting everyone’s time? Yes, we can, and &lt;strong&gt;we can even display that the test took 25 minutes to run, even if it actually took a few seconds.&lt;/strong&gt; These are special effects applied to deliberate practice!&lt;/p&gt;
&lt;p&gt;Let’s see how I’ve recreated an almost real environment for that kata.&lt;/p&gt;
&lt;h2 id=&quot;using-an-almost-external-library-and-upgrading-it&quot; tabindex=&quot;-1&quot;&gt;Using an almost external library and upgrading it&lt;/h2&gt;
&lt;p&gt;As mentioned before, we need multiple library versions, and students should be able to upgrade to the next version using composer, the PHP package manager.&lt;/p&gt;
&lt;p&gt;The first trick here is to use a built-in functionality of composer. Instead of hosting the package online, I kept it inside the repo. The kata comes with all the &lt;code&gt;akei/kulinarisk&lt;/code&gt; library versions.&lt;/p&gt;
&lt;p&gt;With composer, you can register custom repositories for packages. &lt;strong&gt;One of these &lt;a href=&quot;https://getcomposer.org/doc/05-repositories.md#path&quot;&gt;custom repository types, &lt;code&gt;path&lt;/code&gt;&lt;/a&gt;, allows you to map a local directory to a package at a specific version.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;composer.json&lt;/code&gt; of the kata &lt;code&gt;repositories&lt;/code&gt; points to all the necessary versions of &lt;code&gt;akei/kulinarisk&lt;/code&gt;. They are stored in the same repository as the kata code.&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;//composer.json&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;repositories&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./libs/Kulinarisk_V1.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;canonical&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;options&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;versions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;          &lt;span class=&quot;token property&quot;&gt;&quot;akei/kulinarisk&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;path&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./libs/Kulinarisk_V2.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;canonical&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;options&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token property&quot;&gt;&quot;versions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;          &lt;span class=&quot;token property&quot;&gt;&quot;akei/kulinarisk&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Participants install the project and run &lt;code&gt;composer install&lt;/code&gt; when the kata starts. They get &lt;code&gt;akei/kulinarisk:1.0&lt;/code&gt;. Later, they can run a &lt;code&gt;composer require akei/kulinarisk&lt;/code&gt; to upgrade to the V2 and feel the pain of having code coupled with vendor code.&lt;/p&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Keeping libraries in the same repository&lt;/p&gt;
&lt;p&gt;Aside from creating katas, this feature is useful with the modular monolith and mono repo approach. Maybe some modules could use some shared code. Instead of creating a library on another repository, pushing it to packagist (PHP libraries repository), and trying to keep the library private, you could make your life simpler and keep that library inside the same repository as your application code.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The second magic trick I use in the kata allows me to hide some code.&lt;/p&gt;
&lt;h2 id=&quot;the-fake-sleep-method-trick&quot; tabindex=&quot;-1&quot;&gt;The fake &lt;code&gt;sleep&lt;/code&gt; method trick&lt;/h2&gt;
&lt;p&gt;At some point, I hope that participants will look at the Kulinarisk code to understand why it’s so slow. They will notice that some maths is involved in transforming a duration in minutes into seconds, followed by a call to the &lt;code&gt;sleep&lt;/code&gt; function.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;AKEI&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Kulinarisk&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;laga&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$matr&lt;/span&gt;ätt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$varaktighet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$varaktighetISekunder&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$varaktighet&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;br&gt;            &lt;span class=&quot;token function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$varaktighetISekunder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;v&lt;/span&gt;ä&lt;span class=&quot;token function&quot;&gt;rme&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$matr&lt;/span&gt;ätt&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$varaktighetISekunder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As said before, in the story we’re supposed to wait for 25 minutes before getting our warm peppers, but as a facilitator, I don’t want to see people waiting that long each time they run their tests. Here we need to replace a function everyone knows with something else but make it look like we are calling the original one.&lt;/p&gt;
&lt;p&gt;The trick is to &lt;strong&gt;monkey patch with namespaces&lt;/strong&gt;. Alongside the file containing the Kulinarisk class, I have another file declaring a &lt;code&gt;sleep&lt;/code&gt; function in the same namespace as the Kulinarisk class.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;AKEI&lt;/span&gt; &lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token class-name static-context&quot;&gt;TimeLogger&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setExecutionTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$duration&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token function&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;intdiv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;300&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token function&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;intdiv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$duration&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;60&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Because they are in the same namespace, the fake &lt;code&gt;sleep&lt;/code&gt; function takes precedence over the original one.&lt;/strong&gt; Hidden in that fake sleep function, we can add some code to make the tests run slowly but not as slow as the initial sleep implementation.&lt;br&gt;
Note that we’re calling the original sleep function from the global namespace thanks to the &lt;code&gt;&#92;&lt;/code&gt; prefix.&lt;/p&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Monkey patching&lt;/p&gt;
&lt;p&gt;Using a namespace to replace language’s functions can be helpful when creating a test harness on top of legacy code.&lt;/p&gt;
&lt;p&gt;You can quickly introduce a double without touching the code with that technic. This is sometimes called monkey patching and is an example of the Link Substitution pattern described by Michael Feather’s &amp;quot;Working effectively with legacy code&amp;quot;.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;As you can see above in the code of the fake &lt;code&gt;sleep&lt;/code&gt; function, we are passing the expected real sleep time to the &lt;code&gt;TimeLogger::setExecutionTime&lt;/code&gt; method. It’s the first step for our next trick.&lt;/p&gt;
&lt;h2 id=&quot;lying-about-the-test-execution-time&quot; tabindex=&quot;-1&quot;&gt;Lying about the test execution time&lt;/h2&gt;
&lt;p&gt;So far, we’ve seen how to have a consistent story, saying that we will bake something for 25 minutes, calling the sleep method like we’re going to wait for 25 minutes, but wait only a few seconds, to give a feeling of slowness. To make our story consistent, we need to ask: If our tests are calling a method that is expected to run in 25 minutes, how long should they take? 25 minutes, right? How is it that the test output shows that it only took seconds to execute? That doesn’t make for a compelling story. Of course, we can do something and push our story telling a little further!&lt;/p&gt;
&lt;p&gt;In the end, we would like our test report to display that the test took 25 minutes like so:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./test-run-time.png&quot; alt=&quot;Two tests marked as taking 25 and 2 minutes.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Look at the &lt;code&gt;TimeLogger&lt;/code&gt; class, which is responsible for storing each theoretical sleep duration. That class keeps the value in a static field when we call the &lt;code&gt;setExecutionTime&lt;/code&gt; static method. &lt;code&gt;TimeLogger&lt;/code&gt; also offers a static method to access the stored value and a static method to reset the value.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;TimeLogger&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$executionTime&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;resetTimer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword static-context&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$executionTime&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getExecutionTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword static-context&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$executionTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;setExecutionTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$executionTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword static-context&quot;&gt;static&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$executionTime&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$executionTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We’ve seen where the value is stored. Let’s now discuss where we need to read and reset the value.&lt;/p&gt;
&lt;p&gt;So, where? Well, directly inside the test framework. Yes, that’s really hacky, but it does the job.&lt;/p&gt;
&lt;p&gt;Inside the &lt;a href=&quot;https://github.com/sebastianbergmann/phpunit/blob/9.6/src/Framework/TestResult.php#L641&quot;&gt;&lt;code&gt;TestResult::run&lt;/code&gt; method from PHPUnit&lt;/a&gt;, which runs every test, we add an if statement. If the &lt;code&gt;TimeLogger&lt;/code&gt; contains a non-null value, we use that value instead of the value recorded by the PHPUnit timer. Also, we reset the recorded value to null before running each test.&lt;/p&gt;
&lt;p&gt;Now, when a test exercise the &lt;code&gt;Kulinarisk::laga&lt;/code&gt; method, which in turn calls our fake &lt;code&gt;sleep&lt;/code&gt; function, a value is recorded, and we use that value for the duration time display. If a test doesn’t exercise the &lt;code&gt;Kulinarisk::laga&lt;/code&gt; method, the recorded value stays null, and we keep the real test duration time.&lt;/p&gt;
&lt;p&gt;We found a way to display the information we want. One little problem still needs to be solved. The two modifications we’ve made are on vendors&#39; files, which aren’t versioned in the repository. That means that &lt;strong&gt;when participants get the repo and run &lt;code&gt;composer install&lt;/code&gt; to get the dependencies they don’t have that dirty hack.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;patching-dependencies&quot; tabindex=&quot;-1&quot;&gt;Patching dependencies&lt;/h2&gt;
&lt;p&gt;It’s now time for the final interesting trick: modify the PhpUnit code when the library is installed.&lt;/p&gt;
&lt;p&gt;To do this, we use &lt;a href=&quot;https://github.com/cweagans/composer-patches&quot;&gt;&lt;code&gt;cweagans/composer-patches&lt;/code&gt;&lt;/a&gt;, &lt;strong&gt;a package that applies patches when we install another package&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We need to create a patch file containing the difference between the code from the repo and the code we would like to have. I’ve cloned the PHPUnit repository, made the change I needed, ran &lt;code&gt;git diff&lt;/code&gt;, stored the result in a file in the kata repository.&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;diff --git a/src/Framework/TestResult.php b/src/Framework/TestResult.php&lt;br&gt;index 4fde29fac..6be437905 100644&lt;br&gt;&lt;span class=&quot;token coord&quot;&gt;--- a/src/Framework/TestResult.php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token coord&quot;&gt;+++ b/src/Framework/TestResult.php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token coord&quot;&gt;@@ -37,6 +37,8 @@&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;use SebastianBergmann&#92;ResourceOperations&#92;ResourceOperations;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;use SebastianBergmann&#92;Timer&#92;Timer;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;use Throwable;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;use AKEI&#92;TimeLogger;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;/**&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt; * @internal This class is not covered by the backward compatibility promise for PHPUnit&lt;br&gt;&lt;/span&gt;&lt;/span&gt;@@ -693,6 +695,8 @@ function_exists(&#39;xdebug_start_function_monitor&#39;);&lt;br&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;            xdebug_start_function_monitor(ResourceOperations::getFunctions());&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        }&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        TimeLogger::resetTimer();&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        $timer = new Timer;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        $timer-&gt;start();&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;@@ -772,7 +776,8 @@ function_exists(&#39;xdebug_start_function_monitor&#39;);&lt;br&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;            $error = true;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        }&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        $time = $timer-&gt;stop()-&gt;asSeconds();&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        $time = TimeLogger::getExecutionTime() ?? $timer-&gt;stop()-&gt;asSeconds();&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        $test-&gt;addToAssertionCount(Assert::getCount());&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also configured &lt;code&gt;cweagans/composer-patches&lt;/code&gt; to apply the patch file every time Phpunit is installed in the vendor directory like so:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;// composer.json&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;extra&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token property&quot;&gt;&quot;patches&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token property&quot;&gt;&quot;phpunit/phpunit&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;./patches/patch.diff&quot;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Patching dependencies&lt;/p&gt;
&lt;p&gt;Patching dependencies can be necessary when you have some legacy code.&lt;/p&gt;
&lt;p&gt;Maybe you’re blocked with an old language version and would like to use a library that could be available to you if you changed a few things or your code relies on a bug that is now fixed in the library.&lt;/p&gt;
&lt;p&gt;With a patch, you can keep your code running and use vendor code without needing to maintain your own fork and see it diverge from the original library.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;After applying all these tricks, kata participants can clone the kata repository, run &lt;code&gt;composer install&lt;/code&gt;, get the patched version of PHPUnit, the V1 of &lt;code&gt;akei/kulinarisk&lt;/code&gt;, a fake sleep method they don’t see, tests pretending to run in 25 minutes even if they actually run in a few seconds. This is magic!&lt;/p&gt;
&lt;p&gt;Even if creating special effects for kata is fun, the interest of this article is in sharing technics and tools you can apply in your daily life. &lt;strong&gt;The monkey patching technic and the patching package are helpful if you’re dealing with legacy code.&lt;/strong&gt; Keep in mind that it’s not because you can use these ideas that you should stop improving your code. &lt;strong&gt;The composer loading library from a local path can help if you use a mono-repo approach and remove the pain of working across many repositories.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I hope you enjoyed that story!&lt;/p&gt;
&lt;p&gt;Anyway, if you have trouble dealing with a legacy codebase and think that you could use some help &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let&#39;s have a chat&lt;/a&gt; and see what we can do together to help you out of that situation.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Display a message when you enter a directory</title>
      <link href="https://blog.charlesdesneuf.com/articles/display-a-message-when-you-enter-a-directory/"/>
      <updated>2023-02-20T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/display-a-message-when-you-enter-a-directory/</id>
      <summary>I kept forgetting about the usage of a tool and decided to display a help message automatically as soon as I entered the directory. This way, I stop losing time and feeling bad about myself. Here is how!</summary>
      <content type="html">&lt;p&gt;A few days ago, I was doing something I do from time to time with a CLI tool. The command requires a bunch of parameters, and I usually go through my command history to find the previous usage, change only the part that needs to be changed and run the command. That’s efficient, and that works, but every time I do this, I think to myself: &amp;quot;I should have something simpler, just an easy command that takes only one argument; you should create a bash script.&amp;quot;&lt;/p&gt;
&lt;p&gt;Last time I finally decided to create that batch script. I opened a new file in my text editor, copied and pasted the command from my bash history, and looked one more time at how to use arguments in a bash script and what is the proper environment-based shebang. Relatively easy. I tried to save my new script, &amp;quot;download&amp;quot; would be a good name for that command, so I entered &amp;quot;download&amp;quot; in the file name input and tried to save.&lt;/p&gt;
&lt;p&gt;Error.&lt;/p&gt;
&lt;p&gt;Another file already existed with that name. I opened the file and compared it to my new file; they looked identical. It turns out I already automatized my task away and made it simpler but always failed to remember that I did it. &lt;strong&gt;This means every time I used the original script, I was losing time and feeling bad about not taking care of it despite having already dealt with the issue.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I already have that script but keep forgetting about it. What could be the next step to avoid the issue and fix the problem once and for all?&lt;/p&gt;
&lt;p&gt;I usually entered that directory only to run that one task&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. Is it possible to be reminded that I already have a script and can use it directly without needing to go through the bash history?&lt;/p&gt;
&lt;p&gt;It turns out you can.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The solution is to alias the &lt;code&gt;cd&lt;/code&gt; command to a custom command that enters the directory, looks for a file, and displays its content.&lt;/strong&gt; That’s a dead simple solution to my problem.&lt;/p&gt;
&lt;p&gt;Here is the code I took from StackOverflow for the new command and alias creation that you should place in your alias file.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function-name function&quot;&gt;reminder_cd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;builtin&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$@&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; .cd-reminder &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;cat&lt;/span&gt; .cd-reminder &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;1&lt;/span&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;&amp;amp;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;alias&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;cd&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;reminder_cd&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After that, I added a &lt;code&gt;.cd-reminder&lt;/code&gt; file in my project directory with a message explaining that I could use the &lt;code&gt;download&lt;/code&gt; command instead of searching and editing past commands.&lt;/p&gt;
&lt;p&gt;As a freelancer, I can think of a few usages for that tool. Because I go from project to project, I constantly need to remember which toolkit is used or what command I need to start a project.  Now, I can place the information I usually need when I enter the directory in the &lt;code&gt;.cd-reminder&lt;/code&gt; file, and boom.&lt;/p&gt;
&lt;p&gt;By the way, it’s a good idea to add the &lt;code&gt;.cd-reminder&lt;/code&gt; in a global &lt;code&gt;.gititgnore&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I think we can find other usages of that tool for teams. From the top of my mind, I think the quick how-to can work here as well - even if teams in a company should invest in standardizing their tooling to reduce cognitive load. I also believe it could be used to display some messages about the project. Just another way to broadcast information to team members.&lt;/p&gt;
&lt;p&gt;It’s still in the experiment phase on my side, but let me know if you find that idea useful and what you’re using it for.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;No, I can’t automate it further and run it automatically as I can’t possibly know the parameter before. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Quickly jump to periodic file in Obsidian</title>
      <link href="https://blog.charlesdesneuf.com/articles/quickly-jump-to-periodic-file-in-obsidian/"/>
      <updated>2023-05-13T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/quickly-jump-to-periodic-file-in-obsidian/</id>
      <summary>Automate weekly report access in Obsidian using Templater and QuickAdd plugins. Create date-based files automatically and access them instantly without remembering complex filenames.</summary>
      <content type="html">&lt;p&gt;I&#39;m currently working with a client to unstuck a team blocked by a legacy codebase and complicated work processes. I send a weekly report of things we&#39;ve done and things I think we should work on to make help us move forward to a few people in the company.&lt;/p&gt;
&lt;p&gt;I&#39;m using Obsidian as my note-taking application, trying to build some sort of a second brain, and I, of course, write my reports there, to keep track of them.&lt;/p&gt;
&lt;p&gt;Working with reports was a bit annoying because as I have a report per week, I have a new file name every week made from the date of my last day working with a client, and I had trouble quickly accessing it to add new information.  I could use the week number for the filename, but I&#39;m the kind of person who keeps track of time in weeks, which means I never know what the current week is.&lt;/p&gt;
&lt;p&gt;I wanted to find a way to open the current week&#39;s report quickly, and I did an experiment using &lt;a href=&quot;https://silentvoid13.github.io/Templater/&quot;&gt;Templater&lt;/a&gt;, an Obsidian plugin, and it works. Even better, the first time I access the weekly report and it doesn&#39;t exist yet a new report is created in the correct directory with my basic weekly report template.&lt;/p&gt;
&lt;p&gt;I know that other plugins help to manage recurrent notes, such as &lt;a href=&quot;https://github.com/liamcain/obsidian-periodic-notes&quot;&gt;Periodic Notes&lt;/a&gt;. Still, I&#39;m already using the weekly periodic note system for something else, and it would lose all the fun of the experiment.&lt;/p&gt;
&lt;h2 id=&quot;the-template&quot; tabindex=&quot;-1&quot;&gt;The template&lt;/h2&gt;
&lt;p&gt;The idea is to use a Templater template, which comes with a few interesting functions, to make everything work. When I&#39;m somewhere in the application, I can use a shortcut to introduce the template, the template is processed, and the logic is executed. In case the report doesn&#39;t exist, it creates it, and in any case, takes me to it.&lt;/p&gt;
&lt;p&gt;Here is the template with some comments&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Generate the file name. I&#39;m not working on Friday&#39;s with that client&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// so I end my week on Thursdays =&gt; start of week + 3 days&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fileName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;moment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;startOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Week&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;d&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;[W]WW-YYYY-MM-DD&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Where Weekly reports are stored&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; weeklyReportDirectory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;🦺 Projects/MY-CLIENT/Weekly reports&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fullFileName &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; weeklyReportDirectory &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; fileName&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Try to access the file&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; file &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;find_tfile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fileName&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// The file exists, open it&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;workspace&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getLeaf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;openFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// The file doesn&#39;t exist&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Get the report template&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; template &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; tp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;find_tfile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;🦺 Projects/MY-CLIENT/Templates/Weekly Report.md&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Create a new file based on the template&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; newReport &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; tp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create_new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;template&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fileName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; folder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;vault&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getAbstractFileByPath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;weeklyReportDirectory&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; tp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;create_new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;template&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fileName&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; folder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;making-it-work-from-everywhere&quot; tabindex=&quot;-1&quot;&gt;Making it work from everywhere&lt;/h2&gt;
&lt;p&gt;Unfortunately, Obsidian&#39;s template system can&#39;t append a template when no file is opened. This is clearly a minor issue as I have almost always one file opened, but I wanted to access the report quickly in any situation. Also, even if the solution is based on a template, using the template shortcut to select a template to go to another file felt weird. In my opinion, it would be better to have that quick access right in the Obsidian main action menu.&lt;/p&gt;
&lt;p&gt;The solution to my problem is to use the QuickAdd plugin.&lt;br&gt;
With &lt;a href=&quot;https://quickadd.obsidian.guide/docs/&quot;&gt;QuickAdd&lt;/a&gt;, you can capture elements to a page, format these elements using a template, and run the capture macro from the main menu.&lt;/p&gt;
&lt;p&gt;Here is the recipe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;In QuickAdd, create a capture macro&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Capture to active file&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Select &lt;code&gt;Capture format&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Copy-Paste the template created before in the text area&lt;/li&gt;
&lt;li&gt;In Quick Add main menu, click the lightning strike icon to add a command to the main menu&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, I&#39;m able to open the Obsidian main action menu, select the Quick Add command and access the weekly report.&lt;/p&gt;
&lt;p&gt;I don&#39;t need to think about the date and try to guess the filename to access my weekly reports. A little brain power saved each time!&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Causal Loop Diagram Asymmetrical Level Pattern</title>
      <link href="https://blog.charlesdesneuf.com/article/system-thinking-causal-loop-asymmetrical-level-pattern/"/>
      <updated>2023-06-06T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/article/system-thinking-causal-loop-asymmetrical-level-pattern/</id>
      <summary>Discover how asymmetrical patterns in causal loop diagrams reveal opportunities to zoom in or zoom out. Learn to identify when direct and indirect paths indicate missing details or unnecessary complexity.</summary>
      <content type="html">&lt;p&gt;I participated in a workshop on System Thinking hosted by Kent Beck (Yep, that &lt;a href=&quot;https://twitter.com/KentBeck&quot;&gt;Kent Beck&lt;/a&gt;) and Jessica Kerr (Yep, the real &lt;a href=&quot;https://twitter.com/jessitron&quot;&gt;Jessitron&lt;/a&gt;) during NCraft this year.&lt;/p&gt;
&lt;p&gt;We draw a lot of causal loop diagrams all together and in sub-groups.&lt;/p&gt;
&lt;p&gt;In some diagrams we drew, I started spotting a pattern I found strange, something bothering my appetite for symmetry.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphviz&quot;&gt;---
className: diagram-25
---
strict digraph { 
  A -&amp;gt; B
  B -&amp;gt; C
  A -&amp;gt; C
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From time to time, we drew diagrams indicating that :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;more of &lt;code&gt;A&lt;/code&gt; leads to more of &lt;code&gt;B&lt;/code&gt;, and more of &lt;code&gt;B&lt;/code&gt; leads to more of &lt;code&gt;C&lt;/code&gt;, and&lt;/li&gt;
&lt;li&gt;more of &lt;code&gt;A&lt;/code&gt; leads to more of &lt;code&gt;C&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;What felt strange to me is that these diagrams described in two ways that &lt;code&gt;C&lt;/code&gt; influences &lt;code&gt;A&lt;/code&gt;. One directly and another indirectly, via &lt;code&gt;B&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It looks like path 1, &lt;code&gt;A -&amp;gt; B -&amp;gt; C&lt;/code&gt;,  and path 2, &lt;code&gt;A -&amp;gt; C&lt;/code&gt;, are not on the same level of abstraction. Why do you, on one side, care about that intermediate B step and not on the other?&lt;/p&gt;
&lt;p&gt;Seeing that pattern pushes me toward two options, Zooming Out and Zooming In.&lt;/p&gt;
&lt;h2 id=&quot;zoom-out&quot; tabindex=&quot;-1&quot;&gt;Zoom Out&lt;/h2&gt;
&lt;p&gt;The first option is that the diagram still conveys enough information for our discussion if we remove &lt;code&gt;B&lt;/code&gt;. We are then left with a simpler diagram, showing that more of &lt;code&gt;A&lt;/code&gt; leads to more of &lt;code&gt;C&lt;/code&gt;. We can avoid the clutter and acknowledge that &lt;code&gt;B&lt;/code&gt; wasn’t that relevant to the story we’re telling. We don’t need that level of detail, and we can zoom out.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphviz&quot;&gt;---
className: diagram-25
---
 
strict digraph { 
  A -&amp;gt; C
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But that’s not always that easy. Maybe &lt;code&gt;B&lt;/code&gt; is interesting for our story, and we really don’t want to remove it from the picture.&lt;/p&gt;
&lt;h2 id=&quot;zoom-in&quot; tabindex=&quot;-1&quot;&gt;Zoom In&lt;/h2&gt;
&lt;p&gt;The other option is to start asking questions and try to zoom in. That imbalance prompts us to look for more details. How does &lt;code&gt;A&lt;/code&gt; influence &lt;code&gt;C&lt;/code&gt; in other ways? What parts of this story are we missing?&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphviz&quot;&gt;---
className: diagram-25
---

strict digraph { 
  A -&amp;gt; B
  B -&amp;gt; C
  A -&amp;gt; D
  D -&amp;gt; C
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After a few questions, we might discover that &lt;code&gt;A&lt;/code&gt; influences &lt;code&gt;C&lt;/code&gt; via &lt;code&gt;D&lt;/code&gt;, or even via even more complex relationships and have a different look at our system. By noticing that asymmetry, we might gain a deeper knowledge of the problem we are dealing with, and have greater discussions.&lt;/p&gt;
&lt;h2 id=&quot;zoom-in-and-zoom-out-later&quot; tabindex=&quot;-1&quot;&gt;Zoom In and Zoom Out later&lt;/h2&gt;
&lt;p&gt;Choosing between Zooming Out and Zooming In is not an obvious choice.&lt;/p&gt;
&lt;p&gt;Why did we include &lt;code&gt;B&lt;/code&gt; in the first place? It must have made sense for our discussion and understanding of the system. Before getting rid of it, I feel like we should give a chance to the discovery process and try to Zoom In first. After completing the diagram with the missing parts, we can decide that &lt;code&gt;B&lt;/code&gt; and the new details we’ve uncovered are not worth keeping, and finally, Zoom out.&lt;/p&gt;
&lt;p&gt;Drawing as many diagrams as needed and not being afraid to throw them away, to try different forms and see what works for your current discussion, was one the first pieces of advice Jessica and Kent shared with us.&lt;/p&gt;
&lt;h2 id=&quot;it%E2%80%99s-about-the-direction...&quot; tabindex=&quot;-1&quot;&gt;It’s about the direction...&lt;/h2&gt;
&lt;p&gt;Zooming out is only possible if in both paths, &lt;code&gt;A -&amp;gt; C&lt;/code&gt; and &lt;code&gt;A -&amp;gt; B -&amp;gt; C&lt;/code&gt;, &lt;code&gt;A&lt;/code&gt; influences &lt;code&gt;C&lt;/code&gt; in the same direction.&lt;/p&gt;
&lt;p&gt;If&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;more of  &lt;code&gt;A&lt;/code&gt; leads to less of &lt;code&gt;C&lt;/code&gt;, but&lt;/li&gt;
&lt;li&gt;more of &lt;code&gt;A&lt;/code&gt; leads to more of &lt;code&gt;B&lt;/code&gt;, and more of &lt;code&gt;B&lt;/code&gt; leads to more of &lt;code&gt;C&lt;/code&gt; (so more of &lt;code&gt;A&lt;/code&gt; leads to more of &lt;code&gt;C&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphviz&quot;&gt;---
className: diagram-25
---

strict digraph { 
  A -&amp;gt; B
  B -&amp;gt; C
  A -&amp;gt; C [label=&amp;quot;-&amp;quot;]
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;more of &lt;code&gt;A&lt;/code&gt; leads to more of &lt;code&gt;C&lt;/code&gt;, and&lt;/li&gt;
&lt;li&gt;more of &lt;code&gt;A&lt;/code&gt; leads to more of &lt;code&gt;B&lt;/code&gt;, but more of &lt;code&gt;B&lt;/code&gt; leads to less of &lt;code&gt;C&lt;/code&gt;  (so more of &lt;code&gt;A&lt;/code&gt; leads to less of &lt;code&gt;C&lt;/code&gt;)&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphviz&quot;&gt;---
className: diagram-25
---

strict digraph { 
  A -&amp;gt; B
  B -&amp;gt; C [label=&amp;quot;-&amp;quot;]
  A -&amp;gt; C 
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You definitely should avoid the Zooming Out option and keep that part of the diagram as it is. There is something interesting in the story here. Zooming In could probably be worthwhile to explain how more of &lt;code&gt;A&lt;/code&gt; can lead to more of &lt;code&gt;C&lt;/code&gt; and to less of &lt;code&gt;C&lt;/code&gt; at the same time. You might find an interesting intervention point in your system if you are trying to increase (or decrease) &lt;code&gt;C&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I find it really interesting how noticing a visual pattern of one direct and one indirect path can push us to clarify our drawing or to ask more questions. It is similar to our reflexes while looking at code, noticing patterns that are good opportunities for code improvements or should lead to more inquiry. Being able to pattern match quickly is one of the things you gain with experience (so yay me, more system thinking experience!) and also greatly helps when you are helping others. You quickly notice something and can start asking questions to facilitate the discussion. Maybe that pattern I noticed will be useful to others!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;The same idea applies to more of &lt;code&gt;A&lt;/code&gt; leads to less of &lt;code&gt;B&lt;/code&gt;, but less of &lt;code&gt;B&lt;/code&gt; leads to more of &lt;code&gt;C&lt;/code&gt;. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Remove all services arguments from Symfony config with a script</title>
      <link href="https://blog.charlesdesneuf.com/articles/remove-all-services-arguments-configuration-from-symfony-with-a-script/"/>
      <updated>2023-07-05T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/remove-all-services-arguments-configuration-from-symfony-with-a-script/</id>
      <summary>Creating simple scripts can save a lot of time in manual work. Here is a simple AWK script to remove services&#39; arguments definition from Symfony config files.</summary>
      <content type="html">&lt;p&gt;I&#39;m helping a team migrate from Symfony 3.4 to a newer version of the framework. One of our objectives is to move away from using the container to get access to services and use dependency injection instead. Our first step was enabling auto wiring, which should simplify the rest of the work because the framework will automagically inject every new service we add to the constructor directly into the service—no need to edit any configuration.&lt;/p&gt;
&lt;p&gt;The next step we plan is to use &lt;a href=&quot;https://github.com/rectorphp/rector-symfony/blob/main/docs/rector_rules_overview.md#containergettoconstructorinjectionrector&quot;&gt;Rector to transform every call to &lt;code&gt;Container::get&lt;/code&gt; to dependency injection&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Rector will save us tremendous time by refactoring the code but will not touch the configuration. In the configuration, when a service definition declares the service arguments, it takes precedence over the auto wiring. We need to clean all the configuration files by removing the arguments declaration.&lt;/p&gt;
&lt;p&gt;I decided to see if I could use &lt;code&gt;awk&lt;/code&gt; to script that and avoid the manual work requested on many different config files, and I managed to do so in less time than expected. Practicing some &lt;code&gt;awk&lt;/code&gt; katas was worth it, after all.&lt;/p&gt;
&lt;p&gt;Here is the script. It looks for &lt;code&gt;arguments:&lt;/code&gt; and sets a variable indicating that we are in an argument block. Once in that block, we look for the first line not starting with &lt;code&gt;-&lt;/code&gt; to say we are out of the argument block. The script prints every line outside arguments blocks, when &lt;code&gt;ARGUMENT_BLOCK == 0&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;BEGIN &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;    ARGUMENT_BLOCK &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;ARGUMENT_BLOCK &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;/&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;:blank:&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;*-/ &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;    ARGUMENT_BLOCK &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;/arguments:/ &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;    ARGUMENT_BLOCK &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;ARGUMENT_BLOCK &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;print &lt;span class=&quot;token variable&quot;&gt;$0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here is an example of a diff once the script was used on a &lt;code&gt;services.yaml&lt;/code&gt; file. Note that the script deals with the single and multiline YAML array format.&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token coord&quot;&gt;@@ -2,29 +2,12 @@&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;    sdk.entity.list:&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        class: XXX&#92;ListsRepository&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        factory: [ &#39;@doctrine.orm.default_entity_manager&#39;, &#39;getRepository&#39; ]&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        arguments:&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;            - &#39;SDKListBundle:Lists&#39;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;    sdk.service.list:&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        class: XXX&#92;ListBundle&#92;Services&#92;Lists&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        arguments:&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;            - &#39;@service_container&#39;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;            - &#39;@event_dispatcher&#39;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;            - &#39;@sdk.entity.list&#39;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;    sdk.entity.list_access_set:&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        class: XXX&#92;Repository&#92;ListAccessSetRepository&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        factory: [ &#39;@doctrine.orm.default_entity_manager&#39;, &#39;getRepository&#39; ]&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;        arguments: [&#39;SDKListBundle:ListAccessSet&#39;]&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;    sdk.service.list_access_set:&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;          class: XXX&#92;Services&#92;ListAccessSet&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;          arguments:&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;              - &#39;@sdk.entity.list_access_set&#39;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;              - &#39;@sdk.entity.mapping_list_access_set&#39;&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;              - &#39;@logger&#39;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&#92; No newline at end of file&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To run the script on a single file you can use &lt;code&gt;awk -i inplace -f script file&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And if you have a lot of &lt;code&gt;services.yaml&lt;/code&gt; files to edit at once, you can use a combination of &lt;code&gt;find&lt;/code&gt;, &lt;code&gt;xargs&lt;/code&gt; and &lt;code&gt;awk&lt;/code&gt;:&lt;br&gt;
&lt;code&gt;find ./src -name services.yml | xargs awk -i inplace -f script&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;That step is now automated; we don&#39;t need to edit hundreds or more service definitions manually. A few minutes of work and a lot of time saved.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Expected, actual, and super fast assertion creation</title>
      <link href="https://blog.charlesdesneuf.com/articles/expected-actual-and-super-fast-assertion-creation/"/>
      <updated>2023-07-23T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/expected-actual-and-super-fast-assertion-creation/</id>
      <summary>Boost test-writing productivity with PhpStorm live templates and postfix templates. Learn to create expected/actual variables instantly and generate assertions with minimal keystrokes using custom IDE automation.</summary>
      <content type="html">&lt;p&gt;If you follow this blog&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; you know that one thing I enjoy is &lt;a href=&quot;/articles/coder-ses-tests-sans-les-mains-grace-aux-live-templates-de-phpstorm/&quot;&gt;improving my workflow inside my IDE&lt;/a&gt;. The feeling of productivity of being able to code something in seconds instead of minutes brings me joy and lets me keep my focus on other things. I recently had a new improvements idea for test creation&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;I think that a great idea to use in tests is to prefix variables with a special role and have a convention for it. My personal preference is to prefix the variable containing the expected result with ‘expected’ and the one containing the value you want to check with ‘actual’.&lt;/p&gt;
&lt;p&gt;I decided to create two &lt;a href=&quot;https://plugins.jetbrains.com/plugin/9862-custom-postfix-templates&quot;&gt;custom postfix templates&lt;/a&gt; that, for some value, create a variable with the wanted prefix and trigger auto-completion to generate the end of the variable name.&lt;/p&gt;
&lt;p&gt;The flow goes like this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a value: &lt;code&gt;new Pony(‘Eole’)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Type &lt;code&gt;.ev&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Get &lt;code&gt;$expected&amp;lt;cursor&amp;gt; = new Pony(‘Eole’);&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Let the autocompletion guide you. Here it will typically suggest something interesting based on the value, like &lt;code&gt;Pony&lt;/code&gt; in our example. If you’re dissatisfied with the suggestion, type something else.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Tab ⇥&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Done.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;See it in action here:&lt;br&gt;
&lt;img src=&quot;ev.gif&quot; alt=&quot;Example of running the ev postfix template&quot;&gt;&lt;/p&gt;
&lt;p&gt;I let you imagine how it works with the &lt;code&gt;av&lt;/code&gt; postfix template to get an &lt;code&gt;actual&lt;/code&gt; prefixed variable.&lt;/p&gt;
&lt;p&gt;It took me a few minutes to get a variable name in camelCase because the auto-completion returns a capitalized name. Thankfully, you can use GroovyScript inside templates, which makes it easy to lower the generated string.&lt;/p&gt;
&lt;p&gt;You can avoid the work of recreating those two postfix templates by copying the following snippet in the plugin configuration file.&lt;/p&gt;
&lt;pre class=&quot;language-groovy&quot;&gt;&lt;code class=&quot;language-groovy&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ev &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Create an expected variable&lt;br&gt;	ANY &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;expected&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;var&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;capitalize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;groovyScript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;if(_1) { _1.substring(1) } else { _1 }&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;phpSuggestVariableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;expr&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;END&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;av &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Create an actual variable&lt;br&gt;	ANY &lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;actual&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;var&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;capitalize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;groovyScript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;&quot;if(_1) { _1.substring(1) } else { _1 }&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;phpSuggestVariableName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;expr&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;END&lt;span class=&quot;token punctuation&quot;&gt;$&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But that’s just the first part of the improvement.&lt;/p&gt;
&lt;p&gt;Now that I had a convenient way to generate my expected and actual value, it unlocked a new idea: let’s generate assertions with the variable already there.&lt;/p&gt;
&lt;p&gt;For this, I created a new live template, &lt;code&gt;asseq&lt;/code&gt; which adds the code for an equality assertion, &lt;code&gt;$this-&amp;gt;assertEquals($expected, $actual);&lt;/code&gt; and triggers autocomplete after the two prefilled parameters. Getting an assert equal in a test is now as easy as typing &lt;code&gt;asseq&lt;/code&gt;, &lt;code&gt;Tab ⇥&lt;/code&gt;, &lt;code&gt;Tab ⇥&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;See it here how fast it goes :&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;asseq.gif&quot; alt=&quot;Example of running the asseq live template&quot;&gt;&lt;/p&gt;
&lt;p&gt;And here is the live template snippet to add to your configuration if you want to use that trick to.&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;asseq&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;$$this-&lt;span class=&quot;token entity named-entity&quot; title=&quot;&amp;gt;&quot;&gt;&amp;amp;gt;&lt;/span&gt;assertEquals($$expected$VAR$, $$actual$VAR2$);&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toReformat&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toShortenFQNames&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;VAR&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;complete()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;VAR2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;complete()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PHP Statement&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As a bonus, here is the snippet for a second live template that allows you to select what assertion you want because you sometimes need something other than an equality assertion. It does the same thing as the previous one but adds an extra stop and autocomplete step to select the assertion.&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;assert&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;$$this-&lt;span class=&quot;token entity named-entity&quot; title=&quot;&amp;gt;&quot;&gt;&amp;amp;gt;&lt;/span&gt;assert$ASSERT$$END$($$expected$VAR$, $$actual$VAR2$);&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toReformat&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;toShortenFQNames&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;VAR&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;complete()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;VAR2&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;complete()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;variable&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ASSERT&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;expression&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;complete()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;defaultValue&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alwaysStopAt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;option&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;PHP Statement&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;context&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope this blog post will give you other ideas on how to use live templates and postfix templates to improve you workflow and bring more joy to your work life.&lt;/p&gt;
&lt;p&gt;As you can see, I enjoy &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;teaching about testing&lt;/a&gt; or help dev teams being more productive and working in a safer way. If you feel like your team could enjoy imrpving the way it works &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let&#39;s have a chat&lt;/a&gt; and see what we can do together.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;You don’t, let’s be honest, no one does 😅 &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;If you followed that blog, you would know that I spend a lot of time speaking about testing. If that’s one of you’re areas of interest, &lt;a href=&quot;/tags/testing/&quot;&gt;go there&lt;/a&gt;. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>PipDecks Iphone Widget</title>
      <link href="https://blog.charlesdesneuf.com/articles/pipdecks-iphone-widget/"/>
      <updated>2023-08-23T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/pipdecks-iphone-widget/</id>
      <summary>Randomly display a PipDeck card on a widget of your iPhone Home Screen.</summary>
      <content type="html">&lt;p&gt;I started using &lt;a href=&quot;https://readwise.io/&quot;&gt;Readwise&lt;/a&gt; a few months ago. The app allows you to highlight articles or books you read and aims to help memory retention by resurfacing content regularly. One way to do this is by displaying a widget on your phone screen. I installed the Readwise widget on my iPhone and found the idea to be brilliant. It could be part of the solution for me reading articles on the same topic repeatedly.&lt;/p&gt;
&lt;p&gt;I wondered what other content I could display on widgets. My first choice was to randomly display a note from my Obsidian vault. I decided to try it using &lt;a href=&quot;https://scriptable.app/&quot;&gt;Scriptable&lt;/a&gt;, an iOS automation app that uses Javascript. However, I needed markdown parsing and I had no idea how to easily accomplish it. This led me to another idea.&lt;/p&gt;
&lt;p&gt;I had purchased some &lt;a href=&quot;https://pipdecks.com/selrahcd&quot;&gt;PipDecks&lt;/a&gt; (this is an affiliate link) in the past, and very recently with the addition of the new Strategy Deck, it gave me an idea. Would it be possible to display a random card in a widget? The goal here is the same as with Readwise: resurface content, remember it, and make new connections.&lt;/p&gt;
&lt;p&gt;Using Scriptable, I created, with the help of ChatGPT, a script displaying the front of a card, and when clicked, it would take you to the back of the card image inside the iPhone’s file manager. I encountered a problem where my widget had an ugly white background, but after some googling, I discovered that &lt;a href=&quot;https://gist.github.com/mzeryck/3a97ccd1e059b3afa3c6666d27a496c9&quot;&gt;Maxwell Zeryck created a base Scriptable script to create a transparent background widget&lt;/a&gt;. After merging my script with Maxwell’s code, voilà!&lt;/p&gt;
&lt;p&gt;Here’s how the widget looks and works:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;social.gif&quot; alt=&quot;The widget in action&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;how-to-install&quot; tabindex=&quot;-1&quot;&gt;How to Install&lt;/h2&gt;
&lt;p&gt;If you want to install this widget on your phone, you can &lt;a href=&quot;/articles/2023-08-23-pipdecks-card-iphone-widget/assets/pipdeck-widget-script.txt&quot;&gt;download the script&lt;/a&gt;. The code may not be perfect, but it gets the job done.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create a directory where it makes sense to you in the File app on your iPhone.&lt;/li&gt;
&lt;li&gt;Download your PipDecks cards from &lt;a href=&quot;https://pipdecks.com/pages/vault&quot;&gt;PipDecks Vault&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Copy some PipDecks cards into that directory. You need both the front and back of all cards, and all cards must be directly within that directory. You must remove every intermediate directory (see &lt;a href=&quot;#bonus%3A-installing-decks-with-a-directory-per-card&quot;&gt;bonus below&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Install &lt;a href=&quot;https://scriptable.app/&quot;&gt;Scriptable&lt;/a&gt;, if you haven’t already.&lt;/li&gt;
&lt;li&gt;In the Scriptable configuration, create a bookmark named &amp;quot;PipDecks&amp;quot; pointing to the directory containing the cards. Don’t change that name, it’s hardcoded in the script.&lt;/li&gt;
&lt;li&gt;Create a new Scriptable script. Pick a name you like. &amp;quot;PipDecks Card Widget&amp;quot; is a good one.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/articles/2023-08-23-pipdecks-card-iphone-widget/assets/pipdeck-widget-script.txt&quot;&gt;Copy my script&lt;/a&gt; and paste it inside the Scriptable editor and save.&lt;/li&gt;
&lt;li&gt;Create a screen capture of your iPhone on an empty Home Screen Page. We will use this to generate the widget background.&lt;/li&gt;
&lt;li&gt;Go back to Scriptable, run the script. It will ask you to provide the screen capture you just took, and to select if you want to place your widget at the top or the bottom of the screen.&lt;/li&gt;
&lt;li&gt;Add a new widget on one of your Home Screen Pages and select the script you’ve just created.&lt;/li&gt;
&lt;li&gt;Enjoy!&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;bonus%3A-installing-decks-with-a-directory-per-card&quot; tabindex=&quot;-1&quot;&gt;Bonus: Installing Decks with a Directory per Card&lt;/h2&gt;
&lt;p&gt;When you download cards from PipDecks Vaults, you’ll notice that some decks are organized differently from others. Some have all cards in the same directory (which is what we want) and others have a sub-directory for each card. Instead of undergoing the manual and tedious task of going inside each card directory to copy the front and back images to the bookmarked &amp;quot;PipDecks&amp;quot; directory, I created an iPhone shortcut which prompts for an origin directory, loops through all cards directories, and moves the images to the bookmarked directory. It’s much easier.&lt;/p&gt;
&lt;p&gt;If you prefer a simpler approach, you can &lt;a href=&quot;https://www.icloud.com/shortcuts/b08cfa69a8f841cfb2e105ed9495851b&quot;&gt;download the shortcut&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I hope you’ll enjoy the widget and that it will help you remember all the great ideas from your PipDecks.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>How test frameworks work: A basic mental model</title>
      <link href="https://blog.charlesdesneuf.com/articles/how-test-frameworks-work-a-basic-mental-model/"/>
      <updated>2023-09-13T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/how-test-frameworks-work-a-basic-mental-model/</id>
      <summary>Understanding how test frameworks work help writing better test. Here is the start of a simple mental model.</summary>
      <content type="html">&lt;p&gt;Understanding how your test framework works is a good start to avoid making mistakes. In this post, I share my basic mental model of how most test frameworks work. This is not 100% accurate, but it is sufficient to understand a few things about writing better tests.&lt;/p&gt;
&lt;p&gt;In the following posts, we will continue growing that mental model and see why some features of testing frameworks exist.&lt;/p&gt;
&lt;h1 id=&quot;a-loop-over-functions...&quot; tabindex=&quot;-1&quot;&gt;A loop over functions...&lt;/h1&gt;
&lt;p&gt;You can view most test frameworks as a loop over functions. Because it’s a loop, some tests are run before others. That’s not a super impressive statement, but it has some consequences we’ll see later. For now, remember that we’ve got something that loops and that it’s the first component of our test framework.&lt;/p&gt;
&lt;p&gt;In pseudo-code, here is the main loop for a test framework:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;tests &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tests &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;...-throwing-exceptions...&quot; tabindex=&quot;-1&quot;&gt;... throwing exceptions...&lt;/h2&gt;
&lt;p&gt;The second part of the test framework is assertions. We use assertions to validate that everything went as expected while running the code.&lt;br&gt;
To communicate if the result was ok or not, assertions use exceptions. If something is okay, assertions do nothing; if something goes wrong, they throw an exception.&lt;/p&gt;
&lt;p&gt;Here is some pseudo-code for the superstar &lt;code&gt;assertEqual&lt;/code&gt; assertion :&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token function&quot;&gt;assertEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TestAssertionException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;/span&gt; and &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$b&lt;/span&gt;&lt;/span&gt; are not equal&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You get a green bar if no assertions throw an exception inside the loop, meaning that your system behaved according to your tests. Hurray!&lt;/p&gt;
&lt;p&gt;The basics are as simple as that: a loop over functions that may throw exceptions.&lt;/p&gt;
&lt;p&gt;But keeping it like this would not provide a great developer experience because the loop would stop at the first exception thrown. You can only know how many of your tests are failing by fixing them individually and each time relaunching your test runner to discover if you still have errors or have just fixed the last problem.&lt;/p&gt;
&lt;h2 id=&quot;...-and-a-try%2Fcatch.&quot; tabindex=&quot;-1&quot;&gt;... and a try/catch.&lt;/h2&gt;
&lt;p&gt;To solve that problem, the test framework encapsulates the test function inside a &lt;code&gt;try/catch&lt;/code&gt; block. Now, when something doesn’t go as expected, the assertion still throws an exception, which bubbles up the test function and is caught by the try/catch block inside the loop.&lt;/p&gt;
&lt;p&gt;There, the exception is stored in a list of exceptions caught for every test, and that list will later be used to display all error messages.&lt;/p&gt;
&lt;p&gt;Even if one or more tests fail, all tests are run. Also, the test framework is now able to display one error for each of the failing tests.&lt;/p&gt;
&lt;p&gt;The pseudo-code for the enhanced test framework is as follows. We’ve added a &lt;code&gt;try/catch&lt;/code&gt; encapsulating the test execution and a second loop for exception display.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;tests &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;failedTestExceptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Test loop&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tests &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; exception&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Error display&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;displayGreenBar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;failedTestExceptions &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;displayErrorFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And now, we have an understanding of how basic test frameworks work. Obviously, real test frameworks are more complex than that simple mental model, and they offer more functionalities.&lt;/p&gt;
&lt;p&gt;In the following posts, we will see &lt;a href=&quot;/articles/how-test-frameworks-work-a-basic-mental-model-expecting-exception/&quot;&gt;why following the Arrange-Act-Assert pattern is impossible when we verify that the system under tests throws an exception&lt;/a&gt;open . We will also understand why the &lt;code&gt;tearDown&lt;/code&gt; functionality is more important than the &lt;code&gt;setup&lt;/code&gt; one and discuss the &amp;quot;one assertion per test&amp;quot; rule from a technical point of view.&lt;/p&gt;
&lt;p&gt;As you can see, I enjoy teaching about testing. If you are looking to improve your test suites I think I can help. Have a look at &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;my video course&lt;/a&gt; in French or &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let’s have a chat&lt;/a&gt; and see what we can do together.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>How test frameworks work: Expecting that an exception is thrown and the Arrange-Act-Assert pattern.</title>
      <link href="https://blog.charlesdesneuf.com/articles/how-test-frameworks-work-a-basic-mental-model-expecting-exception/"/>
      <updated>2023-10-15T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/how-test-frameworks-work-a-basic-mental-model-expecting-exception/</id>
      <summary>Let’s expand our mental model of the way test frameworks work and see how the expecting that an exception is thrown is done. This will explain why we can’t follow Arrange-Act-Assert pattern for that purpose.</summary>
      <content type="html">&lt;p&gt;In &lt;a href=&quot;/articles/how-test-frameworks-work-a-basic-mental-model/&quot;&gt;the previous article&lt;/a&gt;, we built a basic mental model of how test frameworks work. Now, it’s time to expand on that model to understand why it’s not possible to follow the Arrange-Act-Assert test arrangement pattern for tests that expect an exception to be thrown.&lt;/p&gt;
&lt;p&gt;Let’s look at the code of our simple test framework in the state we left it previously.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;tests &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;failedTestExceptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Test loop&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tests &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; exception&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Error display&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;displayGreenBar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;failedTestExceptions &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;displayErrorFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;let%E2%80%99s-throw-some-exceptions.&quot; tabindex=&quot;-1&quot;&gt;Let’s throw some exceptions.&lt;/h2&gt;
&lt;p&gt;Imagine now that you want to assert that the system under test throws an exception under certain conditions. We can add a new test to the test list, which throws an exception to mimic the fact that the system under test throws one, like so:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	throws MySuperException&lt;br&gt;	&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and add it to the test list&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;tests &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;		throws MySuperException&lt;br&gt;	&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the test framework executes the test, the exception is thrown. This is the behavior we expect. The exception is caught by the test framework and added to the collection of failedTestException. Even if the system under test behaved as expected and correctly threw an exception, the test is marked as failing.&lt;/p&gt;
&lt;h2 id=&quot;ok%2C-so-we-need-to-tell-the-framework-that-we-expect-this-to-happen.&quot; tabindex=&quot;-1&quot;&gt;OK, so we need to tell the framework that we expect this to happen.&lt;/h2&gt;
&lt;p&gt;We need to find a way to tell the test framework that we expect an exception so that he can avoid considering the test as failing, and we need to do this before the exception is thrown. Otherwise, the test will fail. We can achieve this by storing the expected exception in a variable. Now, when the SUT throws the exception, we can compare it with the expected one, if set, and avoid storing the exception as an evidence of a failing test.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;tests &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		expectedException &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MySuperException&lt;br&gt;&lt;br&gt;		throws MySuperException&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;failedTestExceptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Test loop&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tests &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	expectedException &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exception &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; expectedException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;			failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; exception&lt;br&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Error display&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;displayGreenBar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;failedTestExceptions &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;displayErrorFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that &lt;code&gt;expectedException&lt;/code&gt; variable is reset to null before every test to ensure that we will stop tracking that that exception is thrown for the following tests to be run. Without that part, the error would be masked if MySuperException is thrown by the SUT incorrectly in a subsequent test. Also, we are storing information about what exception is expected and not only that some exception is expected because we want to avoid masking failure if an exception that is not &lt;code&gt;MySuperException&lt;/code&gt; is thrown.&lt;/p&gt;
&lt;p&gt;As you can see, we have to tell the framework that an exception is thrown before the moment it will be thrown because it wouldn’t be able to compare them otherwise and decide what to do. This explains why you, unfortunately, have to stay away from the AAA pattern when testing that the SUT throws an exception.&lt;/p&gt;
&lt;p&gt;The other important information you get with that mental of model of test frameworks expect exception is that you have to be super suspicious when you see a test where the expected exception is declared after the Act part of the test. The test is probably not testing what you think and will likely never fail because the exception goes missing &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;h2 id=&quot;and-what-if-the-expected-exception-is-not-thrown-after-all%3F&quot; tabindex=&quot;-1&quot;&gt;And what if the expected exception is not thrown after all?&lt;/h2&gt;
&lt;p&gt;So far, our basic test framework doesn’t fail when the expected exception is thrown. That’s only the first half of the problem. Suppose now that the exception is not thrown. That would mean that our SUT doesn’t behave as expected, and this is something we would like to know.&lt;/p&gt;
&lt;p&gt;We need to complement our test framework so that it can tell us that an expected exception wasn’t thrown. To achieve this, we add a &lt;code&gt;continue&lt;/code&gt; statement in the &lt;code&gt;try/catch&lt;/code&gt; block. If an exception is thrown, expected or not, we deal with it in the catch block and move on to the next test—nothing more to do here.&lt;/p&gt;
&lt;p&gt;If no exception was thrown, we must ensure we weren’t expecting one.  After the &lt;code&gt;try/catch&lt;/code&gt; block, a place we should not reach if an exception was thrown, thanks to the &lt;code&gt;continue&lt;/code&gt; instruction, we can check if an exception was expected. If we expected an exception, if &lt;code&gt;expectedException&lt;/code&gt; variable is not null, we add to the list of errors that we never received the expected exception.&lt;/p&gt;
&lt;p&gt;The pseudo-code looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;tests &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		expectedException &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MySuperException&lt;br&gt;&lt;br&gt;		throws MySuperException&lt;br&gt;	&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;failedTestExceptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Test loop&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tests &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	expectedException &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exception &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; expectedException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;			failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; exception&lt;br&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Did we get our expected exception?&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expectedException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NeverReceivedExpectedException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expectedException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Error display&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;displayGreenBar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;failedTestExceptions &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;displayErrorFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our mental model of the inner workings of test frameworks continues growing as we add more features to it. We now understand how choosing exceptions as the communication mechanism to tell that an assertion failed blocks us from using the Arrange-Act-Assert pattern when we want to assert that an exception is thrown.&lt;/p&gt;
&lt;p&gt;That suite of articles on building a mental model about test frameworks will continue. In the next one, we will dive into the necessity of the &lt;code&gt;tearDown&lt;/code&gt; method.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;And was probably written after the code. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Change the problem to write simpler tests.</title>
      <link href="https://blog.charlesdesneuf.com/articles/change-the-problem-to-write-simpler-tests/"/>
      <updated>2023-11-15T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/change-the-problem-to-write-simpler-tests/</id>
      <summary>Learn how to simplify difficult test scenarios by reframing the problem you&#39;re solving. Transform time-dependent, hard-to-test code into easily testable solutions through strategic problem redefinition.</summary>
      <content type="html">&lt;p&gt;Sometimes, writing tests for a piece of code is difficult but can be simplified by changing the problem we are trying to solve.&lt;/p&gt;
&lt;p&gt;A very good example of this I know is about testing something time-related. Controlling time is something we cannot do. No matter what we try, time passes. We could use tools to control the system&#39;s clock and see where it goes, but we sometimes can find a simpler solution just by having another perspective about the problem we&#39;re trying to solve.&lt;/p&gt;
&lt;p&gt;Imagine that you&#39;re working on a food inventory system that can tell if a dish is edible right now.&lt;/p&gt;
&lt;p&gt;For instance, Panna Cottas can be eaten two days after they&#39;ve been cooked. One possible way to implement that logic is the following code:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;PannaCotta&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;DateTimeImmutable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$cookingDate&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;isEdible&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$now&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTimeImmutable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;now&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$twoDaysAfterCookingTime&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;cookingDate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;modify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;+ 2 days&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$now&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$twoDaysAfterCookingTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That code is pretty straightforward, but the dependency on time with the call to &lt;code&gt;new DateTimeImmutable(&#39;now&#39;)&lt;/code&gt; makes it difficult to test.&lt;/p&gt;
&lt;p&gt;A test that ensures that a Panna Cotta cooked less than two days ago is edible will start failing at some point (in two days max, actually) if we hard-code the value of the cooking date. We could write our test to use a relative date, but they make tests harder to understand and can cause flakiness. That&#39;s not a satisfying solution either.&lt;/p&gt;
&lt;p&gt;A better solution is to look for a solution to control the date used for the comparison, and this is where changing the problem can help us to write more straightforward test. Instead of trying to know if a panna cotta is edible right now, we can change the problem to ask if a panna cotta is edible at some date. After all, you might want to know if the panna cotta you have will still be edible tomorrow and, if so,  choose something else for dessert right now.&lt;/p&gt;
&lt;p&gt;Once we decide to move to that new problem, we can modify the &lt;code&gt;PannaCotta&lt;/code&gt; class code to this:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;PannaCotta&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;__construct&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name type-declaration&quot;&gt;DateTimeImmutable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$cookingDate&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;isEdibleOn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;DateTimeImmutable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$potentialEatingDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$twoDaysAfterCookingTime&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;cookingDate&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;modify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;+ 2 days&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$potentialEatingDate&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$twoDaysAfterCookingTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We&#39;ve renamed the &lt;code&gt;isEdible&lt;/code&gt; method to &lt;code&gt;isEdibleOn&lt;/code&gt; and have introduced a &lt;code&gt;DateTimeImmutable&lt;/code&gt; parameter representing the date we might eat the panna cotta, &lt;code&gt;$potentialEatingDate&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Testing that code is now a piece of cake, because we control the cooking date and the date at which we might eat the panna cotta:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;PannaCottaTest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;	 * @test&lt;br&gt;	 */&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;is_edible_up_to_two_days_after_cooking&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token class-name type-declaration&quot;&gt;DateTimeImmutable&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$potentialEatingDate&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$pannaCotta&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PannaCotta&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTimeImmutable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;2017-05-18&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;		&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertTrue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;			&lt;span class=&quot;token variable&quot;&gt;$pannaCotta&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;isEdibleOn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DateTimeImmutable&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;20127-05-19&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we didn&#39;t have to use some fancy tooling to create the test. Just a change of perspective and reframing the problem.&lt;/p&gt;
&lt;p&gt;Dan North wrote &lt;a href=&quot;https://dannorth.net/the-mystery-of-the-missing-date/&quot;&gt;an article about rethinking the problem we&#39;re trying to solve and discovering a missing concept in the domain&lt;/a&gt; you might be interested in as well. Funnily enough, it&#39;s also about time.&lt;/p&gt;
&lt;p&gt;Forcing us to think harder about the problem is a really lovely side-effect of testing. When a test is difficult to write, seems odd to read, forces us to instantiate things we are not going to use anyway - something we can detect when we see a dummy (&lt;a href=&quot;https://blog.charlesdesneuf.com/articles/le-dummy-un-indice-pas-si-con/&quot;&gt;an article by me about that, in French&lt;/a&gt;)- it&#39;s a good clue that we should take a few minutes to rethink the problem or our design.&lt;/p&gt;
&lt;p&gt;If you need some help with your test I think I can help. Have a look at &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;my video course&lt;/a&gt; in French or &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let&#39;s have a chat&lt;/a&gt; and see what we can do together.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Creating databases when starting the Postgres Docker container</title>
      <link href="https://blog.charlesdesneuf.com/articles/creating-databases-when-starting-the-postgres-docker-ontainer/"/>
      <updated>2023-11-21T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/creating-databases-when-starting-the-postgres-docker-ontainer/</id>
      <summary>We usually need to setup multiple databases to work locally, at least one for development and one for testing. Here is a cool trick to create them automatically when the Postgres docker container starts..</summary>
      <content type="html">&lt;p&gt;One of the first things we usually ask people to do when they set up a project is to create the needed databases. While we often directly use the default database for development to avoid that problem, it remains that we would like at least one other database for testing purposes. Using the same database for development and testing is a nightmare of data inconsistencies.&lt;/p&gt;
&lt;p&gt;The PostgreSQL Docker container allows a nice trick to avoid any manual steps. It’s entry point, the script that is run when the container starts, looks for scripts inside a specific directory, &lt;code&gt;/docker-entrypoint-initdb.d&lt;/code&gt;, and runs them.&lt;/p&gt;
&lt;p&gt;You can mount an SQL script to create the testing database if it doesn’t exist yet.&lt;br&gt;
To create a ‘testing’ database automatically, you can use the following script:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;CREATE DATABASE testing&#39;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;EXISTS&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; pg_database &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; datname &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;testing&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&#92;gexec&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is an example of what to change in à &lt;code&gt;docker-compose.yaml&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; postgres&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;latest&lt;br&gt;    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;    &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; ./infrastructure/database/pgsql/create&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;testing&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;database.sql&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;/docker&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;entrypoint&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;initdb.d/10&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;create&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;testing&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;database.sql&lt;/code&gt;&lt;/pre&gt;
&lt;div class=&quot;admonition warning&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;The entry point only runs the scripts when the storage volume is empty&lt;/p&gt;
&lt;p&gt;Adding a script to create the testing database on an existing project where the database container already has a volume will not work. Two solutions here: run the script by hand or drop the volume and restart the container.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;I learned that trick working on a project using Laravel Sail, so thank you to them!&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Testing that generated PDFs are visually the same</title>
      <link href="https://blog.charlesdesneuf.com/articles/testing-that-generated-PDFs-are-visually-similar/"/>
      <updated>2023-12-03T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/testing-that-generated-PDFs-are-visually-similar/</id>
      <summary>A cool trick to test that your PHP app correctly generates the PDFs you expect</summary>
      <content type="html">&lt;p&gt;We need to generate various PDF files for the project I’m currently working on. Some of them are around 50 pages long. With such large documents, missing that we made a change that messes with the output has a high probability of happening. We wanted to automate discovering mistakes and avoid the tedious task of looking at no-so-interesting PDFs - most of them are contracts - after every change and decided to add some automated tests. Bonus point if our tests are capable of discovering that the newly created PDFs are not the same as the expected ones but also to show us what is different. And we did it.&lt;/p&gt;
&lt;p&gt;We created a custom assertion we can use inside PhpUnit. That assertion is in a trait, which means we can use it in multiple testing classes.&lt;br&gt;
&lt;strong&gt;That assertion uses &lt;a href=&quot;https://vslavik.github.io/diff-pdf/&quot;&gt;&lt;code&gt;diff-pdf&lt;/code&gt;&lt;/a&gt;, a CLI tool for visually comparing PDFs.&lt;/strong&gt; If you want to use the assertion, you must install it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The assertion is inspired by &lt;a href=&quot;/tags/approval-testing/&quot;&gt;Approval Testing&lt;/a&gt; and only takes the path to the new PDF as a parameter. It then compares that file with a stored PDF with the new one. If they are not visually similar the assertion fails.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Here is an example of its usage:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;GeneratePDFTest&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestCase&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PdfAssertion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**  &lt;br&gt;     * @test  &lt;br&gt;     */&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;generate_the_PDF_document_with_the_information_we_want&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$PDFGenerator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PDFGenerator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$pdfFilePath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$PDFGenerator&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;generateFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Charles&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;verifyPDF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$pdfFilePath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;First, use the &lt;code&gt;PdfAssertion&lt;/code&gt; trait, and then, in your tests, generate a PDF and call the &lt;code&gt;verifyPDF&lt;/code&gt; method with the path of the newly generated PDF.&lt;/p&gt;
&lt;p&gt;When you run the test, the generated PDF is copied to a file ending with &lt;code&gt;.received.pdf&lt;/code&gt;  in the &lt;code&gt;approval&lt;/code&gt; directory at the same level as the test file. That file is compared with the file ending with &lt;code&gt;.approved.pdf&lt;/code&gt; in that same directory. If they match, the test passes; if they don’t, the test fails. You must keep the &lt;code&gt;.approved.pdf&lt;/code&gt; as it is the reference for future test runs. The &lt;code&gt;*.received.pdf&lt;/code&gt; pattern can be added to your &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The first time you run the test, you’ll get an error explaining that no file was previously approved and that you need to review the generated file and create an approved file if you are satisfied with its content. The error message even gives you the copy command to run to create the approved file.&lt;/p&gt;
&lt;p&gt;If the two files do not match and the test fails, the tool will produce a file ending with &lt;code&gt;.diff.pdf&lt;/code&gt; showing you the differences between them.&lt;br&gt;
That’s super convenient. There is no need to read all that long contract to discover what changed!&lt;br&gt;
These &lt;code&gt;*.diff.pdf&lt;/code&gt; should also be excluded from versioning.&lt;/p&gt;
&lt;p&gt;Below is a diff of a PDF supposed to render “Bravo Charles” that actually rendered “Bravo Champion.” The end of “Charles” is red colored, and the end of “Champion” is in cyan.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./diff.png&quot; alt=&quot;A visual diff example&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here is the code of the trait:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;PdfAssertion&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;/**  &lt;br&gt;     * @var int[]  &lt;br&gt;     */&lt;/span&gt;&lt;br&gt;     &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword type-declaration&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$PDFVerifiedInTestCount&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;verifyPDF&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$pdfFilePath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertFileExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$pdfFilePath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;The PDF file doesn&#92;&#39;t exist&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;incrementPDFVerificationInTestCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;copyToReceivedFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$pdfFilePath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ensureApprovalDirectoryExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;          &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ensureApprovedFileExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;verifyThatPDFsAreMatching&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;verifyThatPDFsAreMatching&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$approvedPDFFilePath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approvedFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$receivedPDFFilePath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receivedFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$diffedPDFFileName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;diffedFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$command&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;diff-pdf --output-diff=&quot;%s&quot; &quot;%s&quot; &quot;%s&quot; 2&gt;&amp;amp;1&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$diffedPDFFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$approvedPDFFilePath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$receivedPDFFilePath&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$output&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$resultCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$resultCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token function&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;  &lt;br&gt;                &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EOS&lt;/span&gt;  &lt;br&gt;The generated &lt;span class=&quot;token constant&quot;&gt;PDF&lt;/span&gt; file is not the same &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; the control pdf&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; Diff is visible here&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;s  &lt;br&gt;&lt;span class=&quot;token constant&quot;&gt;EOS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;br&gt;                &lt;span class=&quot;token variable&quot;&gt;$diffedPDFFileName&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;ensureApprovedFileExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$approvedPDFFilePath&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approvedFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$receivedFileName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receivedFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;assertFileExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$approvedPDFFilePath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sprintf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;EOS&lt;/span&gt;  &lt;br&gt;No approved file exist &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PDF&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&#92;&lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;  &lt;br&gt;Please review that file &lt;span class=&quot;token keyword&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; you are satisfied with its content&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; copy it to &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&#92;&lt;span class=&quot;token variable&quot;&gt;$s&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;  &lt;br&gt;cp &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;%1&#92;$s&quot;&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;%2&#92;$s&quot;&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token constant&quot;&gt;EOS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$receivedFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$approvedPDFFilePath&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;ensureApprovalDirectoryExists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$approvalDirectory&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSnapshotDirectory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is_dir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$approvalDirectory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$approvalDirectory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;incrementPDFVerificationInTestCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$testName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cleanTestName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;array_key_exists&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$testName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;PDFVerifiedInTestCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;          &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;PDFVerifiedInTestCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$testName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;PDFVerifiedInTestCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$testName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;currentPDFVerificationCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$testName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;int&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;PDFVerifiedInTestCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$testName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;copyToReceivedFile&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$pdfFilePath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token function&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$pdfFilePath&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;receivedFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;approvedFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;approved&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;diffedFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;diff&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;receivedFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;received&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;fileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$suffix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$approvalDirectory&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getSnapshotDirectory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$cleanTestName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cleanTestName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$approvalDirectory&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; ‘&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;’&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$cleanTestName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; ‘&lt;span class=&quot;token constant&quot;&gt;_&lt;/span&gt;’&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;currentPDFVerificationCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;  &lt;br&gt;                &lt;span class=&quot;token variable&quot;&gt;$cleanTestName&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; ‘&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;’&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$suffix&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; ‘&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;pdf’&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getSnapshotDirectory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReflectionClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getFileName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token constant&quot;&gt;DIRECTORY_SEPARATOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;  &lt;br&gt;            ‘approval’&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;cleanTestName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword static-context&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cleanFilename&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;nameWithDataSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;cleanFilename&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword type-hint&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$raw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$file&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;preg_replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;([^&#92;w&#92;s&#92;d&#92;-_~,;&#92;[&#92;]&#92;(&#92;).])u&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$raw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$file&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;preg_replace&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;([&#92;.]{2,})&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Part of the code is stolen from &lt;a href=&quot;https://github.com/spatie/phpunit-snapshot-assertions&quot;&gt;Spatie’s Snaphots Assertion library&lt;/a&gt;. Thank them!&lt;/p&gt;
&lt;p&gt;If you look at the code, you’ll notice that the error messages are super helpful. They give you the link to the &lt;code&gt;diff.pdf&lt;/code&gt; when the PDFs are not matching and the copy command call you should make after reviewing your first PDF generation.&lt;/p&gt;
&lt;p&gt;I hope this code will help you save some precious time you’re currently spending looking at boring generated documents!&lt;/p&gt;
&lt;p&gt;Cleaning some legacy code is intellectually rewarding; it can even be fun when you use your automated refactoring tools, not to mention that it greatly reduces the risk of mistakes. If you need some help to get started or to deal with your legacy codebase, &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let&#39;s chat&lt;/a&gt; and see how I can help.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Process Behavior Charts - A Christmas Tale</title>
      <link href="https://blog.charlesdesneuf.com/articles/PBC-A-Christmas-Tale/"/>
      <updated>2023-12-17T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/PBC-A-Christmas-Tale/</id>
      <summary>Here is a Christmas Tale about understanding data variation.</summary>
      <content type="html">&lt;p&gt;Last year, around the same period, I was thinking about Process Behavior Charts (PBC), a tool that helps in figuring out when you should or should not react when looking at data. I don’t remember what the trigger was (probably somebody saying to try harder at something), but I thought that this tool should be more widely known.&lt;/p&gt;
&lt;p&gt;Because Christmas was approaching, I decided to create a small Christmas Tale to introduce people to PBCs. And it worked because I know some people now use them at a past company where I worked.&lt;/p&gt;
&lt;h2 id=&quot;pbc%2C-the-short-version&quot; tabindex=&quot;-1&quot;&gt;PBC, the short version&lt;/h2&gt;
&lt;p&gt;Every process, or system, exhibits variation. You don’t finish the same amount of User Stories every week, the defect rate of that machine producing widgets is not always 0.2, and the duration of an appointment with a doctor is not always exactly 15 minutes...&lt;/p&gt;
&lt;p&gt;Variations come in two sorts: noise and signal. Noise is the normal variation you can expect from a stable system. Signal, on the contrary, are variations that demonstrate that your system is unstable, that there is something unknown about your system, and that you should investigate.&lt;/p&gt;
&lt;p&gt;Some people (like, a lot of people) react to every data point they see, no matter if that data point is something that we should expect from the stable behavior of the system or if it’s something more interesting we need to look into. A lot of time is lost looking at numbers, reacting to every variation going up or down. This leads to finger-pointing, time spent trying to understand something you won’t be able to explain, and management helping by asking to work harder...&lt;/p&gt;
&lt;p&gt;PBCs help in sorting what is signal and what is noise. They tell you if your system is stable and predictable. They tell you when you should look into why that data point is better or worse than another to understand what you can improve.&lt;/p&gt;
&lt;h2 id=&quot;my-christmas-tale&quot; tabindex=&quot;-1&quot;&gt;My Christmas Tale&lt;/h2&gt;
&lt;p&gt;So, I wrote a Christmas Tale, in French, more as a joke than anything, and published it on &lt;a href=&quot;https://www.linkedin.com/in/charles-desneuf/&quot;&gt;LinkedIn&lt;/a&gt;. I added a mention to &lt;a href=&quot;https://www.markgraban.com/&quot;&gt;Mark Graban&lt;/a&gt;, who is an expert in the Lean community and who wrote &amp;quot;Measures of Success&amp;quot;, the book that introduced me to PBCs. Mark sent me a message right away to ask me if we could translate the story into English. I, of course, agreed. We exchanged a few emails, improved the story a little, translated it, and published it. Mark even bought the domain name of the fictional Santa Claus company, &lt;a href=&quot;https://www.northpolebox.com&quot;&gt;North Pole Box&lt;/a&gt; - that redirects to his website where he sells the &amp;quot;Measures of Success&amp;quot; book.&lt;/p&gt;
&lt;p&gt;A few weeks after Christmas, I even received a printed copy with the French and English version of the story in my mailbox. Thanks, Mark!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./book.jpg&quot; alt=&quot;The printed book of my Christmas PBC Tale&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;process-behavior-charts---the-christmas-tale&quot; tabindex=&quot;-1&quot;&gt;Process Behavior Charts - The Christmas Tale&lt;/h2&gt;
&lt;p&gt;I’m sure that by now you would like to immerse yourself in that fairy land of PBC and Christmas.&lt;/p&gt;
&lt;p&gt;Here is the English version:&lt;/p&gt;
&lt;iframe src=&quot;/articles/2023-12-17-PBC-A-Christmas-Tale/assets/english.pdf&quot; width=&quot;500&quot; height=&quot;500&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;And here is the French one:&lt;/p&gt;
&lt;iframe src=&quot;/articles/2023-12-17-PBC-A-Christmas-Tale/assets/french.pdf&quot; width=&quot;500&quot; height=&quot;500&quot;&gt;&lt;/iframe&gt;
&lt;h2 id=&quot;want-to-know-more-about-pbcs%3F&quot; tabindex=&quot;-1&quot;&gt;Want to Know More About PBCs?&lt;/h2&gt;
&lt;p&gt;If you want to know more about PBCs, I encourage you to read Mark’s book. The book explains PBCs but also demonstrates why most of the time we use numbers quite poorly, leading to overreaction, pressure, and no real improvement. The downside of the book is that once you’ve read it, a lot of meetings where your colleagues are sharing numbers will feel terribly unproductive to you...&lt;/p&gt;
&lt;p&gt;Also, Daniel Vacanti recently published &amp;quot;&lt;a href=&quot;https://leanpub.com/actionableagilemetricsii&quot;&gt;Actionable Agile Metrics Volume II&lt;/a&gt;&amp;quot; that dives into PBC for flow metrics. I’m currently reading it, and this is a good book as well to understand what PBCs are and how you can use them.&lt;/p&gt;
&lt;p&gt;Of course, the book on that subject is &amp;quot;Understanding Variation&amp;quot; by Donald J. Wheeler, who developed the idea of PBCs and closely worked with Edward Deming. I’ve recently ordered that book, so I can’t tell if it’s an easy read or not. From what I’ve read about it, it appears to be.&lt;/p&gt;
&lt;p&gt;Speaking about Dr. Wheeler, Mark told me that he sent the link to the ebook to him. It’s also a nice gift to know that my silly idea reached the eyes of a legend in statistics and process improvement.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Quickly check for duplication with your IDE refactoring tools</title>
      <link href="https://blog.charlesdesneuf.com/articles/quickly-check-for-duplication-with-your-ide-refactoring-tools/"/>
      <updated>2024-01-05T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/quickly-check-for-duplication-with-your-ide-refactoring-tools/</id>
      <summary>Using your IDE extract method refactoring tool is a cheap way to discover code duplication, or bugs</summary>
      <content type="html">&lt;p&gt;This week, I was looking at some code that I suspected contained duplication, presenting a good refactoring opportunity. I wanted to know if I could eliminate the duplication in the multiple conditionals, or even go a step further and consolidate the conditional logic in one place, where I could edit it to make it simpler.&lt;/p&gt;
&lt;p&gt;Here is that code:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getDataAboutSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name type-declaration&quot;&gt;ASomethingId&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$somethingId&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name return-type&quot;&gt;DataAboutSomething&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// Some other code  &lt;/span&gt;&lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$draftEloquent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$draftEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$draft&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$draftEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$draft&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$draftEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$draft&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$preMarketEloquent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$preMarketEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;token variable&quot;&gt;$preMarket&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$preMarketEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$preMarket&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$preMarketEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$preMarket&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$deliveryEloquent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$actableEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$delivery&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$deliveryEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$delivery&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$deliveryEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$delivery&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$archivedEloquent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$archivedEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$archived&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$archivedEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token variable&quot;&gt;$archived&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$archivedEloquent&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;validation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$archived&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PieceOfData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataAboutSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$draft&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$preMarket&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$delivery&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$archived&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By now, you probably notice the duplication too and are thinking about how to refactor the code.&lt;/p&gt;
&lt;p&gt;But, should you trust your eyes completely? Initially, I trusted my judgment and moved forward with the code. However, let&#39;s pause for a moment here.&lt;/p&gt;
&lt;p&gt;How can you be certain it&#39;s truly duplication?&lt;/p&gt;
&lt;p&gt;Searching for one occurrence to match it with others isn&#39;t effective, as the variable names are different. You could use a regex search, but it&#39;s not straightforward, especially if the indentation varies (as I intentionally did in one of the occurrences).&lt;/p&gt;
&lt;p&gt;So, you&#39;re left with meticulously reading the code.&lt;/p&gt;
&lt;p&gt;Or...&lt;/p&gt;
&lt;p&gt;You could proceed as I did: try extracting a method and rely on your IDE to handle it correctly. If the code is essentially the same, except for variable names, the IDE will extract the method and offer to replace all occurrences of the duplicated code.&lt;/p&gt;
&lt;p&gt;This approach worked, except for one instance. If you examine closely, you&#39;ll notice that the variables in the two if conditions aren&#39;t the same. Did you catch that initially? I didn&#39;t.&lt;/p&gt;
&lt;p&gt;Here is a gif of the process:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;social.gif&quot; alt=&quot;A demo of the refactoring and the discovered non duplicated fragment.&quot;&gt;&lt;/p&gt;
&lt;p&gt;Without the IDE, I might have inadvertently altered the logic of the code. The IDE revealed that what I thought was duplicated four times was actually only three. This discovery led me to discuss the inconsistency with my coworkers, who suspected it was likely a bug and should have been coded like the others.&lt;/p&gt;
&lt;p&gt;So, &lt;strong&gt;I uncovered a bug by leveraging my IDE&#39;s automated refactoring tools and being somewhat lazy&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Additionally, the IDE, by analyzing the syntactic form of the code, recognized that the second block, despite its irregular indentation, had the same meaning as the method I extracted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dealing with legacy code often uncovers insights when experimenting with refactoring tools. Try things out, see where it leads. If you&#39;re not satisfied with the results, you can always revert the changes. Assess if the refactoring aligns with your expectations. It&#39;s an effortless and risk-free approach.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you spot potential duplication in code, try extracting a method. See if the IDE applies the extraction in multiple places or not. You&#39;ll gain insight. And who knows, you might just eliminate the duplication with a few keystrokes.&lt;/p&gt;
&lt;p&gt;Cleaning some legacy code is intellectually rewarding; it can even be fun when you use your automated refactoring tools, not to mention that it greatly reduces the risk of mistakes. If you need some help to get started or to deal with your legacy codebase, &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let&#39;s chat&lt;/a&gt; and see how I can help.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>3 techniques pour gérer l&#39;aléatoire en Approval Testing</title>
      <link href="https://blog.charlesdesneuf.com/articles/3-techniques-pour-gerer-laleatoire-en-approval-testing/"/>
      <updated>2024-03-23T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/3-techniques-pour-gerer-laleatoire-en-approval-testing/</id>
      <summary>Comment mettre en place un filet de sécurité avec des tests pour un système produisant des résultats aléatoires ? Voici 3 techniques !</summary>
      <content type="html">&lt;p&gt;&lt;em&gt;This article is available &lt;a href=&quot;/articles/approval-testing-3-technics-to-deal-with-randomness/&quot;&gt;in English&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Lorsqu&#39;on est confronté à du code legacy, se retrouver face à de l&#39;aléatoire n&#39;est pas ce qu&#39;il y a de plus sympa.&lt;/p&gt;
&lt;p&gt;Mettre en place des tests pour couvrir un système qui produit des résulats aléatoires peut s&#39;avérer assez vite difficile.&lt;/p&gt;
&lt;p&gt;Comme tout le monde, vous voulez que vos tests soient déterministes et vous donnent toujours le même résultat, quand vous n&#39;avez rien changé dans le système.&lt;/p&gt;
&lt;p&gt;Voici trois idées qui peuvent vous aider à gérer l&#39;aléatoire dans le système. Les deux premières techniques sont fondées sur l&#39;idée de supprimer la partie aléatoire du système pendant les tests.&lt;/p&gt;
&lt;h2 id=&quot;bloquer-le-g%C3%A9n%C3%A9rateur-d&#39;al%C3%A9atoire&quot; tabindex=&quot;-1&quot;&gt;Bloquer le générateur d&#39;aléatoire&lt;/h2&gt;
&lt;p&gt;La première solution que je souhaite partager dans cet article consiste à garantir que le générateur aléatoire renvoie toujours les mêmes résultats lorsqu&#39;il est appelé. La réponse ici est de configurer le générateur avec une valeur que vous contrôlez, afin de rendre le générateur déterministe.&lt;/p&gt;
&lt;p&gt;Par exemple, en PHP, vous pouvez appeler la fonction &lt;code&gt;mt_rand&lt;/code&gt;. Plus tard, tout appel à la fonction &lt;code&gt;rand&lt;/code&gt; renverra les mêmes nombres aléatoires dans le même ordre.&lt;/p&gt;
&lt;p&gt;C&#39;est une manière rapide de transformer un système non déterministe en quelque chose qui donne toujours les mêmes résultats.&lt;/p&gt;
&lt;p&gt;Malheureusement, cette solution, tout en étant l&#39;une des façons les plus rapides de résoudre le problème, ne fonctionne que dans certaines situations. Tout d&#39;abord, toutes les langages ne permettent pas de configurer le générateur de nombres aléatoires. Si ce n&#39;est pas possible il vous faute une autre solution.&lt;/p&gt;
&lt;p&gt;Un autre inconvénient de cette solution est que si vous modifiez la structure de votre code et que l&#39;ordre des appels à une méthode aléatoire est changé vous obtiendrez toujours les mêmes valeurs dans le même ordre mais pas dans le même espace logique. Par exemple, prenons deux appels à &lt;code&gt;rand&lt;/code&gt; et assignons le résultat du premier appel à la variable A et du second appel à la variable B. Après avoir configuré le générateur de nombres aléatoires, A et B ont toujours les mêmes valeurs, disons 34 et 1879. Lors d&#39;un refactoring, si nous décidons d&#39;inverser l&#39;ordre d&#39;assignation, nous avons un problème. Le code assigne maintenant la valeur de B avant celle de A ; A vaut maintenant 1879 et B vaut 39. Vous pouvez &lt;a href=&quot;https://onlinephp.io?s=bZDBTsMwDIbPVMo7mKrSutNo6WAFKjTQJA67wXGXrHXXSmlSOenGhDjyFjwdT0KaFiYkpBxi-_f_2b67b6uWecwrO5mbWkkgLgvVrHHH8-OjKjCcwhvzzmYzeKEj1HKPZGq5A1MhmIOCUgmhDn1G1BK1lQYcMmcTTm_7cPsntI_QdCTBX0KtwaptDR7cf-tbyXs_D_NGYq6aBuWItJqewjztDKPe0SmfkLB3UB0Bb1tSey7AoDbMC_C1xdxgYceYOOQinc-T6zSOT-Q4SqLkcnFxlU6sZcBz01mD7J9rOGJdhj-a8wx-CcOpMK8U-CsiRRu5Gks3J9VGLl2rTQ0e49IoNMLQ_PX58cwbBELdCeODg34D&amp;amp;v=8.2.1&quot;&gt;expérimenter cette idée ici&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Que pouvons-nous faire lorsque la configuration du générateur de nombres aléatoires est impossible, ou si nous souhaitons peut-être changer l&#39;ordre des appels à un moment donné ?&lt;/p&gt;
&lt;h2 id=&quot;contr%C3%B4ler-l&#39;al%C3%A9atoire&quot; tabindex=&quot;-1&quot;&gt;Contrôler l&#39;aléatoire&lt;/h2&gt;
&lt;p&gt;L&#39;autre alternative est de reprendre le contrôle sur la génération des valeurs. Comme souvent, ajouter une couche d&#39;abstraction est l&#39;une des solutions disponibles dans notre boîte à outils. Ici, nous souhaitons briser la dépendance directe envers le générateur de valeurs aléatoires et le remplacer par une dépendance que nous pouvons manipuler.&lt;/p&gt;
&lt;p&gt;En termes plus concrets, au lieu d&#39;appeler directement une fonction du langage, nous l&#39;encapsulons, créons un stub que nous pouvons manipuler, et trouvons un moyen de substituer l&#39;implémentation originale avec notre doublure.&lt;/p&gt;
&lt;p&gt;Les techniques de substitution disponibles varient grandement en fonction du langage de programmation utilisé et de l&#39;état du code. Si vous avez la chance de pouvoir injecter des dépendances lors de la construction, c&#39;est une tâche facile. Si ce n&#39;est pas le cas, certains patterns issus de &lt;em&gt;Working Effectively With Legacy Code&lt;/em&gt; par Michael Feathers peuvent s&#39;avérer utiles.&lt;/p&gt;
&lt;p&gt;Cette solution est disponible dans tous les langages. La seule exigence est de se sentir à l&#39;aise avec la manipulation de code sans avoir de tests pour pouvoir reprendre le contrôle.&lt;/p&gt;
&lt;p&gt;Une fois que vous avez le contrôle, vous pouvez décider des valeurs générées.&lt;/p&gt;
&lt;p&gt;Cette solution nous permet de résoudre le problème d&#39;ordre mentionné précédemment. Si vous inversez l&#39;ordre des appels dans le code, vous pouvez changer la configuration de votre stub pour changer l&#39;ordre et maintenir les tests valides.&lt;/p&gt;
&lt;p&gt;Modifier le stub après la création des tests doit être fait avec prudence. Si vous voulez plus de confiance et éviter de toucher aux tests, vous avez une autre alternative. Au lieu de créer une abstraction pour chaque appel au générateur aléatoire, vous pouvez en créer une pour chaque appel. Reprenant l&#39;exemple de la première partie, vous pouvez introduire une abstraction pour générer A et une autre pour générer B. Avoir deux implémentations vous donne un contrôle plus granulaire, et vous pouvez créer deux stubs. Lorsque vous inversez l&#39;ordre des appels, le code appelle toujours les bonnes abstractions et les mêmes stib, on obtient donc le même résultat final.&lt;/p&gt;
&lt;p&gt;Modifier une base de code existante pour introduire des indirections afin de contrôler des valeurs générées aléatoirement n&#39;est pas toujours simple ni nécessaire. Parfois, la valeur générée ne nous importe pas, et nous pouvons tout à fait accepter l&#39;aléatoire tant que nous parvenons à maintenir nos tests déterministes. Et c&#39;est le point de la troisième idée.&lt;/p&gt;
&lt;h2 id=&quot;masquer-l&#39;al%C3%A9atoire&quot; tabindex=&quot;-1&quot;&gt;Masquer l&#39;aléatoire&lt;/h2&gt;
&lt;p&gt;Parfois, la valeur aléatoire n&#39;est pas vraiment importante pour le résultat. Si cela ne nous importe pas, nous pouvons l&#39;éliminer de ce qui est vérifié par une assertion dans le test.&lt;/p&gt;
&lt;p&gt;L&#39;astuce est que vous n&#39;avez pas l&#39;obligation de faire des assertions basées sur les sorties du système sous test telles quelles. À la place, vous pouvez récupérer ces sorties et les transformer pour ne conserver que les informations dont vous avez besoin pour être suffisamment confiant que vous ne cassez rien.&lt;/p&gt;
&lt;p&gt;Lors de la création de tests d&#39;approbation, il est courant d&#39;utiliser un printer, une fonction ou une classe qui transformera les sorties pour les rendre faciles à comprendre. Si une information n&#39;est pas pertinente, le rôle de l&#39;imprimante est de la supprimer et de créer une sortie propre. Vous pouvez alors utiliser cette sortie propre dans l&#39;assertion.&lt;/p&gt;
&lt;p&gt;Quoi de plus simple que de supprimer ce dont on ne se soucie pas ?&lt;/p&gt;
&lt;p&gt;Parfois, vous ne vous souciez pas de la valeur réelle, mais vous voulez vous assurer qu&#39;une valeur est présente. Au lieu de supprimer la partie aléatoire, vous pouvez la remplacer par autre chose en utilisant un scrubber. Si vous voulez vous assurer qu&#39;un UUID est affiché mais que sa valeur ne vous importe pas, vous pouvez utiliser une expression régulière qui va remplacer tous les UUID par autre chose, comme une chaîne &lt;code&gt;UUID&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Un autre cas d&#39;utilisation courant est de détecter qu&#39;une valeur est répétée à plusieurs endroits dans la sortie.&lt;/p&gt;
&lt;p&gt;Continuons avec l&#39;exemple précédent. Vous ne vous souciez toujours pas de la valeur de l&#39;UUID, mais vous voulez vérifier que l&#39;UUID utilisé dans un lien pour accéder à un produit est bien celui du produit, et non un autre.&lt;/p&gt;
&lt;p&gt;Pour cela il faut rendre notre scrubber un peu plus intelligent.&lt;/p&gt;
&lt;p&gt;Lorsqu&#39;un UUID est détecté à l&#39;aide d&#39;une regex, le scrubber génère une nouvelle chaîne, habituellement à partir d&#39;une chaîne de base et d&#39;un incrément (&lt;code&gt;UUID_1&lt;/code&gt;). Ensuite, il remplace toutes les occurrences suivantes de cet UUID par la chaîne générée, incrémente le compteur et répète l&#39;opération pour chaque UUID trouvé dans la sortie. De cette façon, même si vous ne pouvez pas prédéterminer le résultat du système, vous pouvez le transformer en quelque chose de déterministe que vous pouvez utiliser comme référence.&lt;/p&gt;
&lt;p&gt;Le mieux avec cette solution est qu&#39;elle ne nécessite pas de toucher au système legacy. Vous n&#39;avez pas besoin de vous inquiéter de casser quelque chose en créant les tests. Cette méthode est donc très pratique et relativement rapide pour créer de la confiance. Avec un peu de chance, vous pourriez même travailler avec un langage où les outils d&#39;approval testing viennent avec des scrubbers qui font une partie du travail de nettoyage pour vous.&lt;/p&gt;
&lt;p&gt;Comprendre le fonctionnement d&#39;un système utilisant de l&#39;aléatoire et devoir créer un filet de test n&#39;est pas obligatoirement effrayant ou ne doit pas forcément prendre énormément de temps. Avec les trois idées exposées dans cet article, vous devriez être en mesure d&#39;attaquer ce code legacy que vous évitez de refactoriser depuis déjà trop longtemps.&lt;/p&gt;
&lt;p&gt;Si vous souhaitez un coup de main pour attaquer un chantier de reprise de code legacy, &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;discutons-en&lt;/a&gt; et voyons comment je peux vous aider.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Tester les extraits de code dans un livre</title>
      <link href="https://blog.charlesdesneuf.com/articles/tester-les-extraits-de-code-dans-un-livre/"/>
      <updated>2024-04-19T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/tester-les-extraits-de-code-dans-un-livre/</id>
      <summary>Comment mettre en place un filet de sécurité avec des tests pour un système produisant des résultats aléatoires ? Voici 3 techniques !</summary>
      <content type="html">&lt;p&gt;Pour &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;ma formation sur l&#39;amélioration des tests&lt;/a&gt; j&#39;ai mis en place des fiches récapitulatives pour chacun des chapitres. Ces fiches récapitulatives contiennent pas mal de code et je voulais m&#39;assurer que le code soit fonctionnel. Quoi de mieux que des tests pour s&#39;assurer que le comportement est tel qu&#39;attendu ? Plutôt logique dans une formation sur les tests.&lt;/p&gt;
&lt;p&gt;Les fiches sont à destination des étudiants mais j&#39;ai également écrit &lt;a href=&quot;https://formation.charlesdesneuf.com/guide-gratuit-5-idees-pour-ameliorer-la-lisibilite-de-vos-tests-automatises&quot;&gt;un livre blanc partangeant 5 manières d&#39;améliorer la lisibilité et la maintenabilité des tests&lt;/a&gt; en utilisant cette même technique. Récupérez-le pour vous donner une idée du résultat.&lt;/p&gt;
&lt;h1 id=&quot;asciidoc-et-inclusion-de-code&quot; tabindex=&quot;-1&quot;&gt;Asciidoc et inclusion de code&lt;/h1&gt;
&lt;p&gt;Pour écrire les fiches, j&#39;ai décidé d&#39;utiliser &lt;a href=&quot;https://asciidoc.org/&quot;&gt;Asciidoc&lt;/a&gt;, un format textuel présentant des similarités avec le Markdown, principalement pour sa capacité à inclure des fichiers à l&#39;intérieur du texte, et plus particulièrement du code. Même mieux, il est possible de n&#39;inclure qu&#39;une partie d&#39;un fichier, ce qui permet de ne montrer que le code utile à la compréhension.&lt;/p&gt;
&lt;p&gt;Chaque chapitre est stocké dans un dossier différent et le fichier principal pour la fiche s&#39;appelle &lt;code&gt;fiche.adoc&lt;/code&gt;. Il y a également un dossier &lt;code&gt;code&lt;/code&gt; dans ce même dossier qui va contenir les fichiers contenant le code.&lt;/p&gt;
&lt;p&gt;Pour inclure du code, il est possible d&#39;utiliser l&#39;instruction asciidoc suivante:&lt;/p&gt;
&lt;pre class=&quot;language-asciidoc&quot;&gt;&lt;code class=&quot;language-asciidoc&quot;&gt;&lt;span class=&quot;token attributes&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;source&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;php&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;  &lt;br&gt;----  &lt;br&gt;&lt;span class=&quot;token title important&quot;&gt;&lt;span class=&quot;token macro&quot;&gt;&lt;span class=&quot;token function&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;code/code-01.php&lt;span class=&quot;token attributes&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;tag&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;class-facture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;----&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cette instruction va inclure la portion de code entre les tags &lt;code&gt;tag::class-facture[]&lt;/code&gt; du fichier &lt;code&gt;code-01.php&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Ce fichier ressemble peu ou prou à ça:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;namespace&lt;/span&gt; &lt;span class=&quot;token package&quot;&gt;PasDeCalculDansLesTests&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Code_01&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;UneClasseQuOnNeVeutPasMontrerMaisDontOnABesoin&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// tag::class-facture[]  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Facture&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt; &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// end::class-facture[]  &lt;/span&gt;&lt;br&gt;  &lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// tag::class-produit[]  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;Produit&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// end::class-produit[]  &lt;/span&gt;&lt;br&gt;  &lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// tag::testfile[]  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;FactureTest&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name class-name-fully-qualified&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;PHPUnit&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Framework&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;TestCase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// tag::test_calcule_le_total[]&lt;/span&gt;&lt;br&gt;     &lt;span class=&quot;token comment&quot;&gt;/**&lt;br&gt;     * @test  &lt;br&gt;     */&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;calcule_le_total&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// end::test_calcule_le_total[]  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// end::testfile[]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Quelques informations importantes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Comme vous le voyez, ce fichier contient tout le code qui lui est nécessaire afin d&#39;être indépendant. C&#39;est primordial car chaque chapitre du cours est présenté comme une histoire au travers de laquelle on va modifier le code ou les tests. On veut donc pouvoir avoir plusieurs versions du code en parallèle.&lt;/li&gt;
&lt;li&gt;Chaque version du code est contenue dans son propre namespace, ici &lt;code&gt;PasDeCalculDansLesTests&#92;Code_01&lt;/code&gt;, ce qui permet d&#39;éviter les conflits entre les différentes versions du code.&lt;/li&gt;
&lt;li&gt;Le fichier de code contient le code qu&#39;on ne souhaite pas montrer dans la fiche mais qui est pourtant nécessaire au bon fonctionnement du code.&lt;/li&gt;
&lt;li&gt;Il est possible d&#39;utiliser plusieurs tags différents pour montrer plusieurs sections du code. Le snippet précédent ne vas inclure que la classe &lt;code&gt;Facture&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Les tests se trouvent également dans ce fichier, ce qui veut dire qu&#39;ils sont aussi versionnés.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lorsque l&#39;histoire avance et qu&#39;une autre version du code est nécessaire il suffit alors de copier le fichier et de créer le fichier &lt;code&gt;code-02.php&lt;/code&gt; et de changer le namespace. On a alors un nouveau terrain de jeu indépendant.&lt;/p&gt;
&lt;h2 id=&quot;lancer-les-tests&quot; tabindex=&quot;-1&quot;&gt;Lancer les tests&lt;/h2&gt;
&lt;p&gt;Pour lancer les tests facilement, j&#39;ai créé une suite de test PhpUnit qui permet de lancer les tests contenus dans n&#39;importe quel fichier &lt;code&gt;.php&lt;/code&gt; dans le dossier &lt;code&gt;content&lt;/code&gt;, comme on le voit dans cet extrait du fichier &lt;code&gt;phpunit.xml&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;testsuites&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;testsuite&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;All&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;directory&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;.php&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;./content/*&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;testsuite&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;testsuites&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Je peux donc désormais lancer les tests pour m&#39;assurer que les exemples contenus dans les fiches sont corrects.&lt;/p&gt;
&lt;h2 id=&quot;exclure-des-tests&quot; tabindex=&quot;-1&quot;&gt;Exclure des tests&lt;/h2&gt;
&lt;p&gt;Dans certains chapitres, il est intéressant de montrer du code qui fait échouer les tests. Je ne veux pas que ma suite de test échoue pour ces tests à chaque lancement, j&#39;ai donc fait en sorte de pouvoir les exclure facilement.&lt;/p&gt;
&lt;p&gt;J&#39;ai expérimenté avec deux idées.&lt;/p&gt;
&lt;p&gt;La première est d&#39;exclure les fichiers de code contenant les tests cassants de la suite de test, en utilisant la balise &lt;code&gt;&amp;lt;exclude&amp;gt;&lt;/code&gt; de la configuration PhpUnit.&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;testsuites&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;testsuite&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;All&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;directory&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;.php&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;./content/*&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;exclude&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;./content/sections/02-AAA/06-BBB/code/code-01.php&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;exclude&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;testsuite&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;testsuites&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;La seconde est d&#39;utiliser une annotation &lt;code&gt;@group disable&lt;/code&gt; sur les tests que je souhaitais pas lancer et de configurer PhpUnit, là encore dans &lt;code&gt;phpunit.xml&lt;/code&gt;, pour exclure ces tests.&lt;/p&gt;
&lt;pre class=&quot;language-xml&quot;&gt;&lt;code class=&quot;language-xml&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;groups&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;exclude&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;disable&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;exclude&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;groups&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Il s&#39;agit d&#39;un des usages des tags dans ce projet. J&#39;ai également &lt;a href=&quot;https://blog.charlesdesneuf.com/articles/tags-fonctionnalite-oubliee-des-lanceurs-de-tests/&quot;&gt;utilisé les tags&lt;/a&gt; pour pouvoir aisément exclure les tests s&#39;appuyant sur la base de données pour ne pas avoir besoin d&#39;avoir un serveur disponible en permanence.&lt;/p&gt;
&lt;p&gt;Avec ces idées, il est possible de facilement mettre en place des tests pour s&#39;assurer que les exemples de code présent dans vos articles, livres, ou même documentations sont corrects.&lt;br&gt;
D&#39;ailleurs, inclure des fichiers de code ou des fichiers générés par du code permet de créer une documentation vivante, toujours à jour avec le fonctionnement actuel d’un système (en utilisant de l’&lt;a href=&quot;/tags/approval-testing/&quot;&gt;approval testing&lt;/a&gt; comme &lt;a href=&quot;https://sfauvel.github.io/documentationtesting/&quot;&gt;l’explique Sébastien Fauvel&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;malheureusement%2C-j%E2%80%99utilise-du-markdown&quot; tabindex=&quot;-1&quot;&gt;Malheureusement, j’utilise du markdown&lt;/h2&gt;
&lt;p&gt;Si vous utilisez du Markdown, qui ne permet pas l’inclusion de fichier comme le fait Asciidoc, il est possible d’utiliser &lt;a href=&quot;https://github.com/SimonCropp/MarkdownSnippets&quot;&gt;MarkdownSnippets&lt;/a&gt;. Ce programme va créer une copie du fichier markdown original en incluant le code, ce qui peut être fait dans un pipeline de build.&lt;/p&gt;
&lt;p&gt;Je suis vraiment content du rendu des fiches dans la &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;formation&lt;/a&gt; avec cette technique.  Si vous avez besoin d’aide pour mettre en place ce genre d’idées dans vos équipes, &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;parlons-nous&lt;/a&gt;).&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Generating a file without blocking form submission</title>
      <link href="https://blog.charlesdesneuf.com/articles/generating-a-file-without-blocking-form-submission/"/>
      <updated>2024-05-20T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/generating-a-file-without-blocking-form-submission/</id>
      <summary>Build responsive web apps with asynchronous file generation. Learn to accept user input without blocking, generate files in the background, and provide seamless download experiences using CQRS patterns.</summary>
      <content type="html">&lt;p&gt;Last week I was discussing with one teammate at my current client about an issue they had.&lt;/p&gt;
&lt;p&gt;The application generates a document based on some data provided by the user.&lt;br&gt;
He explained to me that we needed to wait for the document to be generated to be able to redirect to the next page, just after form submission, because there is a link to download the document there, which means we need to know where the document is, which means we need to have already created the document.&lt;/p&gt;
&lt;p&gt;At first, it may seem like an intractable issue, but it’s actually not the case.&lt;br&gt;
There is a solution that doesn’t require the document to be generated before redirecting the user to the confirmation page.&lt;br&gt;
That solution, in its most basic form, is relatively simple and doesn’t require a lot of code.&lt;/p&gt;
&lt;p&gt;As we’ll see, we can add some complexity to the solution to improve the user experience.&lt;/p&gt;
&lt;p&gt;You can have access to the code in the &lt;a href=&quot;https://github.com/SelrahcD/file-generation-experiment&quot;&gt;code repository&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;background-story&quot; tabindex=&quot;-1&quot;&gt;Background story&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./images/story-as-user-sees-it.png&quot; alt=&quot;User flow as explain just after&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here is what the story looks like:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The user enters some information&lt;/li&gt;
&lt;li&gt;The server starts generating a file based on that information asynchronously.&lt;/li&gt;
&lt;li&gt;The user can click a link to download the file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;We want the user to have a pleasant experience, so we don’t want to block the form submission until the file is created.&lt;br&gt;
To provide that nice experience, when the form is submitted, the information is stored, an asynchronous process is started for file generation, and the user is redirected to a confirmation page containing a download link.&lt;/p&gt;
&lt;p&gt;If we’re lucky, the file was created by the time the user gets to the confirmation page, and we can show a download link pointing directly to it.&lt;br&gt;
But what if the file was not created yet?&lt;br&gt;
We still want to display the confirmation page as soon as possible. &lt;strong&gt;The first trick is that we actually don’t need the file until the user clicks on the download link.&lt;/strong&gt;&lt;br&gt;
Not needing the file until the user requests the download gives us some extra time to generate it in the background.&lt;/p&gt;
&lt;p&gt;“That’s great, you may say, but we still need a URL to download the file. How can we know the file URL if the file hasn’t been generated?”&lt;br&gt;
This is a good question, and the answer is to use a second trick: &lt;strong&gt;You don’t need the file URL; you need a promise to be able to get that URL at some point.&lt;/strong&gt;&lt;br&gt;
You need a way to ask the server if the file is available and how to get it when it’s ready.&lt;/p&gt;
&lt;p&gt;This repository aims to show how to implement that, evolving the solution from a super simple solution with pure HTML pages to a click-to-download link with a spinner.&lt;br&gt;
Seeing the iterations from the first to the final one helps to understand the pattern to solve that problem.&lt;/p&gt;
&lt;h2 id=&quot;installation-and-run&quot; tabindex=&quot;-1&quot;&gt;Installation and run&lt;/h2&gt;
&lt;p&gt;The demo is a node application using the express framework.&lt;/p&gt;
&lt;p&gt;Start with a git clone from the &lt;a href=&quot;https://github.com/SelrahcD/file-generation-experiment&quot;&gt;code repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Everything should be properly installed with:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Run the application with:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;node&lt;/span&gt; index.js&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;using-the-demo-app&quot; tabindex=&quot;-1&quot;&gt;Using the demo app&lt;/h2&gt;
&lt;h3 id=&quot;you-are-the-worker&quot; tabindex=&quot;-1&quot;&gt;You are the worker&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;⚠️ The background process is faked; you need to manually create the file.&lt;/strong&gt;&lt;br&gt;
Because implementing an asynchronous job is not in the scope of this demo, there is no worker.&lt;br&gt;
To make things convenient, this repo includes two commands to create and delete the file.&lt;br&gt;
Said differently, you are the worker.&lt;br&gt;
This gives you the advantage of experimenting at each step and observing the behavior of each click when the file is there or not.&lt;br&gt;
You decide.&lt;/p&gt;
&lt;p&gt;To create the file, run:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run create-receipt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To remove the file, run:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run clean-receipt&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;there-is-no-form&quot; tabindex=&quot;-1&quot;&gt;There is no form&lt;/h3&gt;
&lt;p&gt;Because there is no worker, there is no need to trigger the worker, which allows us to skip building the form.&lt;br&gt;
The confirmation page is the first page, served at the &lt;code&gt;/&lt;/code&gt; route.&lt;br&gt;
In a real application, you are expected to see this page right after submitting the form.&lt;br&gt;
It displays a link to download a file.&lt;/p&gt;
&lt;h2 id=&quot;iterations&quot; tabindex=&quot;-1&quot;&gt;Iterations&lt;/h2&gt;
&lt;p&gt;I’ve created this demo iterating on the code, starting with the simplest solution.&lt;br&gt;
The cool thing is that it demonstrates how the solution works without JS.&lt;br&gt;
Even better, because it was built gradually, it continues working even if the user browser deactivates JS.&lt;br&gt;
JS is only there to improve the UX, but the application is still functional without it.&lt;/p&gt;
&lt;div class=&quot;admonition experiment&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Play with the demo&lt;/p&gt;
&lt;p&gt;Once you’ve cloned the repository, you can see the code for each iteration with a checkout of the correct tag.&lt;br&gt;
I’ve provided a list of things you can experiment with to see how the system behaves for each iteration in experiment block such as this one.&lt;br&gt;
To get things more interesting, look at the network tab of your browser development tools.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Aside discussions&lt;/p&gt;
&lt;p&gt;In blocks such as this one, I’ve included aside discussions to extend on some of the ideas.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;iteration-1%3A-user-polling&quot; tabindex=&quot;-1&quot;&gt;Iteration 1: User polling&lt;/h3&gt;
&lt;div class=&quot;admonition getcode&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Get the code&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout human-polling&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or &lt;a href=&quot;https://github.com/SelrahcD/file-generation-experiment/blob/human-polling/index.js&quot;&gt;view on Github&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;This iteration is super raw but really helps understand how this works as it requires the user to manually ask (and ask again, if needed) for the file.&lt;/strong&gt;&lt;br&gt;
Future iterations are just UX improvements on top of it.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;sequenceDiagram
actor User
participant confirmation_page as Confirmation page
participant waiting_page as Waiting page
participant receipt_controller as Receipt controller
participant file_download_controller as File download controller
confirmation_page--&gt;&gt;User: 👁️ Display &quot;Download the file&quot; link
User-&gt;&gt;receipt_controller: 👇 Click on &quot;Download the file&quot; [GET]
loop
    alt File doesn’t exist
        receipt_controller--&gt;&gt;waiting_page: returns
        waiting_page--&gt;&gt;User: 👁️ Display &quot;Wait a few seconds and click that link to get the file&quot;
        User-&gt;&gt;receipt_controller: 👇 Click the link [GET]
    else File exists
        receipt_controller-&gt;&gt;file_download_controller: Redirects to [with Location header]
        file_download_controller--&gt;&gt;User : 📄 File
    end
end
&lt;/pre&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; htmlContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;br&gt;    &amp;lt;!DOCTYPE html&gt;&lt;br&gt;    &amp;lt;html lang=&quot;en&quot;&gt;&lt;br&gt;      &amp;lt;head&gt;&lt;br&gt;        &amp;lt;title&gt;Form submitted&amp;lt;/title&gt;&lt;br&gt;      &amp;lt;/head&gt;&lt;br&gt;      &amp;lt;body&gt;&lt;br&gt;        &amp;lt;h1&gt;Your form was submitted !&amp;lt;/h1&gt;&lt;br&gt;        &amp;lt;p&gt;Thank you for your information.&amp;lt;/p&gt;&lt;br&gt;        &amp;lt;a href=&quot;/receipt&quot;&gt;Get your receipt&amp;lt;/a&gt;&lt;br&gt;      &amp;lt;/body&gt;&lt;br&gt;    &amp;lt;/html&gt;&lt;br&gt;  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;htmlContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;br&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/receipt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constants&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;F_OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// File exists&lt;/span&gt;&lt;br&gt;            res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/download&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// File doesn&#39;t exist yet&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; htmlContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;br&gt;    &amp;lt;!DOCTYPE html&gt;&lt;br&gt;    &amp;lt;html lang=&quot;en&quot;&gt;&lt;br&gt;      &amp;lt;head&gt;&lt;br&gt;        &amp;lt;title&gt;File Download&amp;lt;/title&gt;&lt;br&gt;      &amp;lt;/head&gt;&lt;br&gt;      &amp;lt;body&gt;&lt;br&gt;        &amp;lt;h1&gt;File download&amp;lt;/h1&gt;&lt;br&gt;        &amp;lt;p&gt;We are creating your file.&amp;lt;/p&gt;&lt;br&gt;        &amp;lt;p&gt;Please wait 5 seconds and click the download link below:&amp;lt;/p&gt;&lt;br&gt;        &amp;lt;a href=&quot;/receipt&quot;&gt;Download&amp;lt;/a&gt;&lt;br&gt;      &amp;lt;/body&gt;&lt;br&gt;    &amp;lt;/html&gt;&lt;br&gt;  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;htmlContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/download&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;download&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Error during download: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;err&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;500&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Error downloading the file.&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;            console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;File downloaded: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;filePath&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The confirmation page now displays a download link to the &lt;code&gt;/receipt&lt;/code&gt; route.&lt;br&gt;
The &lt;code&gt;/receipt&lt;/code&gt; route controller acts as a traffic cop.&lt;br&gt;
If the file is available, it will redirect to the real file download URL.&lt;br&gt;
Otherwise, it returns a page explaining that the file is still being created and asking the user to wait a few seconds before clicking on a link to download it.&lt;/p&gt;
&lt;p&gt;How can we deal with the file not being available when the user clicks on that second link?&lt;br&gt;
The solution is to repeat that same process and show a page asking to wait and a link to download the file.&lt;br&gt;
Fortunately, we already have a route that behaves this way, the &lt;code&gt;/receipt&lt;/code&gt; one.&lt;br&gt;
Here the solution is that the link on the waiting page is a link to the waiting page.&lt;br&gt;
Users are manully polling to see if the file is available when they click on the link.&lt;/p&gt;
&lt;div class=&quot;admonition experiment&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Experiment&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Create the file and click on the link on the confirmation page&lt;/li&gt;
&lt;li&gt;Create the file once you are on the waiting page and click the link&lt;/li&gt;
&lt;li&gt;Go to the waiting page, click the link, create the file, and click the link again&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Why redirect and not send the file immediately from the receipt route?&lt;/p&gt;
&lt;p&gt;You might think that the redirection overcomplicates the flow, and you want to send the file directly from the receipt route.&lt;br&gt;
Sure, it saves from creating a new route and removes one HTTP request, but you should probably keep that as it is.&lt;/p&gt;
&lt;p&gt;First, the file can be stored elsewhere, on a file storage like AWS S3.&lt;br&gt;
In that case, you don’t want the file to pass through your server and want the file storage to serve it.&lt;br&gt;
A redirection makes a lot of sense here.&lt;/p&gt;
&lt;p&gt;Second, using the same URL for the traffic cop and serving the file will make caching harder.&lt;br&gt;
Having a dedicated URL for the file will help the caching mechanisms detect that they can avoid fetching the file from the server again and again.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;iteration-2%3A-blinking-polling&quot; tabindex=&quot;-1&quot;&gt;Iteration 2: Blinking polling&lt;/h3&gt;
&lt;div class=&quot;admonition getcode&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Get the code&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout blinking-polling&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or &lt;a href=&quot;https://github.com/SelrahcD/file-generation-experiment/blob/blinking-polling/index.js#L41&quot;&gt;view on Github&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The next improvement introduced is to avoid the need for the user to click on the second link on the waiting page.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;sequenceDiagram
    actor User
    participant confirmation_page as Confirmation page
    participant waiting_page as Waiting page
    participant receipt_controller as Receipt controller
    participant file_download_controller as File download controller
    confirmation_page--&gt;&gt;User: 👁️ Display &quot;Download the file&quot; link
    User-&gt;&gt;receipt_controller: 👇 Click on &quot;Download the file&quot; [GET]
    loop
        alt File doesn’t exist
            receipt_controller--&gt;&gt;waiting_page: returns
            waiting_page--&gt;&gt;User: 👁️ Display &quot;Wait a few seconds and click that link to get the file&quot;
            rect rgba(172, 226, 225, 0.4)
                activate waiting_page
                waiting_page-&gt;&gt;waiting_page: ⏱️ Wait for the delay specified in refresh meta
                waiting_page-&gt;&gt;receipt_controller: GET
                deactivate waiting_page
            end
        else File exists
            receipt_controller-&gt;&gt;file_download_controller: Redirects to [with Location header]
            file_download_controller--&gt;&gt;User : 📄 File
        end
    end
&lt;/pre&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/receipt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constants&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;F_OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// File exists&lt;/span&gt;&lt;br&gt;            res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/download&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// File doesn&#39;t exist yet&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; htmlContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;br&gt;    &amp;lt;!DOCTYPE html&gt;&lt;br&gt;    &amp;lt;html lang=&quot;en&quot;&gt;&lt;br&gt;      &amp;lt;head&gt;&lt;br&gt;        &amp;lt;title&gt;File Download&amp;lt;/title&gt;&lt;br&gt;        &amp;lt;meta http-equiv=&quot;refresh&quot; content=&quot;5&quot;&gt;&lt;br&gt;      &amp;lt;/head&gt;&lt;br&gt;      &amp;lt;body&gt;&lt;br&gt;        &amp;lt;h1&gt;File download&amp;lt;/h1&gt;&lt;br&gt;        &amp;lt;p&gt;We are creating your file.&amp;lt;/p&gt;&lt;br&gt;        &amp;lt;p&gt;Please wait 5 seconds and click the download link below:&amp;lt;/p&gt;&lt;br&gt;        &amp;lt;a href=&quot;/receipt&quot;&gt;Download&amp;lt;/a&gt;&lt;br&gt;      &amp;lt;/body&gt;&lt;br&gt;    &amp;lt;/html&gt;&lt;br&gt;  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;htmlContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The simplest solution is to reload the waiting page every N seconds, which is what the user was manually doing by clicking on the link.&lt;br&gt;
Once the file is ready, the download starts.&lt;/p&gt;
&lt;p&gt;This is achieved by using a meta tag to the header:&lt;/p&gt;
&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;http-equiv&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;refresh&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;5&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Still no JS.&lt;/p&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;An information for the HTTP client&lt;/p&gt;
&lt;p&gt;One interesting thing to note with the introduction of that meta tag is that we start differentiating information for the client and the user.&lt;br&gt;
The meta tags is for the browser, the HTTP client, which knows how to interpret it.&lt;br&gt;
The text is for the user, who knows how to interpret that as well.&lt;br&gt;
The two pieces of information don’t have to match.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;admonition experiment&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Experiment&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to the waiting page and wait until the page reloads, then create the file&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h3 id=&quot;iteration-3%3A-polling-without-blinking&quot; tabindex=&quot;-1&quot;&gt;Iteration 3: Polling without blinking&lt;/h3&gt;
&lt;div class=&quot;admonition getcode&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Get the code&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout server-retry-after&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or &lt;a href=&quot;https://github.com/SelrahcD/file-generation-experiment/blob/cc758cd7d5096bff552ac23925d7be0b8e979d52/index.js#L48-L115&quot;&gt;view on Github&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The next improvement is to poll the server to see if the file has been created without reloading the page.&lt;br&gt;
Now is the time for JS to come into play.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;sequenceDiagram
    actor User
    participant confirmation_page as Confirmation page
    participant waiting_page as Waiting page
    participant receipt_controller as Receipt controller
    participant file_download_controller as File download controller
    confirmation_page--&gt;&gt;User: 👁️ Display &quot;Download the file&quot; link
    User-&gt;&gt;receipt_controller: 👇 Click on &quot;Download the file&quot; [GET]

    alt File doesn’t exist
        receipt_controller--&gt;&gt;waiting_page: returns
        waiting_page--&gt;&gt;User: 👁️ Display &quot;Wait a few seconds and click that link to get the file&quot;
        rect rgba(172, 226, 225, 0.4)
            activate waiting_page
            loop
                waiting_page-&gt;&gt;receipt_controller: GET
                alt File doesn’t exist
                    receipt_controller--&gt;&gt;waiting_page: Retry-After
                    waiting_page-&gt;&gt;waiting_page: ⏱️ Wait for the delay specified in Retry-After
                else File exists
                    receipt_controller--&gt;&gt;waiting_page: Content-Disposition: Attachment Location:/download
                    waiting_page-&gt;&gt;file_download_controller: GET
                    file_download_controller--&gt;&gt;waiting_page :  📄 File
                    waiting_page--&gt;&gt;User: 📄 File
                end
            end
            deactivate waiting_page
        end
    else File exists
        receipt_controller-&gt;&gt;file_download_controller: Redirects to [with Location header]
        file_download_controller--&gt;&gt;User : 📄 File
    end
&lt;/pre&gt;
&lt;p&gt;Once the waiting page is displayed, it starts polling the server to get information about the file.&lt;br&gt;
It keeps polling the same route as before using JS &lt;code&gt;fetch&lt;/code&gt; function. We are moving from a space where the browser is the HTTP client to a world where we are creating our own client.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/receipt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constants&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;F_OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// File exists&lt;/span&gt;&lt;br&gt;            res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/download&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// File doesn&#39;t exist yet&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; htmlContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;br&gt;    &amp;lt;!DOCTYPE html&gt;&lt;br&gt;    &amp;lt;html lang=&quot;en&quot;&gt;&lt;br&gt;      &amp;lt;head&gt;&lt;br&gt;        &amp;lt;title&gt;File Download&amp;lt;/title&gt;&lt;br&gt;      &amp;lt;/head&gt;&lt;br&gt;      &amp;lt;body&gt;&lt;br&gt;        &amp;lt;h1&gt;File download&amp;lt;/h1&gt;&lt;br&gt;        &amp;lt;p&gt;We are creating your file.&amp;lt;/p&gt;&lt;br&gt;        &amp;lt;p&gt;Please wait 5 seconds and click the download link below:&amp;lt;/p&gt;&lt;br&gt;        &amp;lt;a href=&quot;/receipt&quot;&gt;Download&amp;lt;/a&gt;&lt;br&gt;      &amp;lt;/body&gt;&lt;br&gt;      &amp;lt;script&gt;&lt;br&gt;&lt;br&gt;      async function retryWithDelay(taskFn, maxRetries = 5) {&lt;br&gt;        let attempt = 0;&lt;br&gt;        let delayMs = 0;&lt;br&gt;    &lt;br&gt;        while (attempt &amp;lt; maxRetries) {&lt;br&gt;            console.log(&#39;Attempt&#39;);&lt;br&gt;            // Attempt to run the task function&lt;br&gt;            const result = await taskFn();&lt;br&gt;            &lt;br&gt;            attempt++;&lt;br&gt;            if (attempt &gt;= maxRetries) {&lt;br&gt;                throw new Error(&#39;Max attempts&#39;)&lt;br&gt;            }&lt;br&gt;            &lt;br&gt;            if(result.type !== &#39;retry-after&#39;) {&lt;br&gt;               return result.value;&lt;br&gt;            }&lt;br&gt;            &lt;br&gt;            delayMs = result.value * 1000;&lt;br&gt;            await new Promise(resolve =&gt; setTimeout(resolve, delayMs));&lt;br&gt;        }&lt;br&gt;    }&lt;br&gt;    &lt;br&gt;        // Function to perform a GET request to the current page&lt;br&gt;       async function pollForFile() {&lt;br&gt;          const response = await fetch(window.location.href);&lt;br&gt;          &lt;br&gt;          if(response.headers.get(&#39;Retry-After&#39;)) {&lt;br&gt;              return {&lt;br&gt;                type: &#39;retry-after&#39;,&lt;br&gt;                value: response.headers.get(&#39;Retry-After&#39;)&lt;br&gt;              };&lt;br&gt;          }&lt;br&gt;          &lt;br&gt;          const contentDisposition = response.headers.get(&#39;Content-Disposition&#39;);&lt;br&gt;          &lt;br&gt;          if(contentDisposition &amp;amp;&amp;amp; contentDisposition.includes(&#39;attachment&#39;)) {&lt;br&gt;           console.log(&#39;The content is marked for attachment download.&#39;);&lt;br&gt;&lt;br&gt;            // Extract filename (if any) from Content-Disposition&lt;br&gt;            const filenameMatch = contentDisposition.match(/filename=&quot;?(.+)&quot;?/);&lt;br&gt;            const filename = filenameMatch ? filenameMatch[1] : &#39;default-filename&#39;;&lt;br&gt;&lt;br&gt;            // Process the response as a Blob and initiate the download&lt;br&gt;            const data = await response.blob();&lt;br&gt;            const downloadUrl = window.URL.createObjectURL(data);&lt;br&gt;            const a = document.createElement(&#39;a&#39;);&lt;br&gt;            a.href = downloadUrl;&lt;br&gt;            a.download = filename;&lt;br&gt;            document.body.appendChild(a);&lt;br&gt;            a.click();&lt;br&gt;            a.remove();&lt;br&gt;            window.URL.revokeObjectURL(downloadUrl);&lt;br&gt;          }&lt;br&gt;          &lt;br&gt;          return {&lt;br&gt;                type: &#39;success&#39;,&lt;br&gt;                value: true&lt;br&gt;              };&lt;br&gt;      }&lt;br&gt;    &lt;br&gt;    retryWithDelay(pollForFile, 10, 5000)&lt;br&gt;        .then(result =&gt; console.log(result))&lt;br&gt;        .catch(error =&gt; console.error(error.message));&lt;br&gt;       &lt;br&gt;      &amp;lt;/script&gt;&lt;br&gt;    &amp;lt;/html&gt;&lt;br&gt;  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Retry-After&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;htmlContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Until the file is ready, a &lt;code&gt;Retry-after&lt;/code&gt; header is added to the response.&lt;br&gt;
It indicates to the client how long it should wait before asking again if the file is ready.&lt;br&gt;
This is the information for the HTTP client.&lt;br&gt;
The JS code interprets that header and retries after the delay proposed by the server.&lt;/p&gt;
&lt;p&gt;Once the file is ready, the waiting route will issue a redirect to the file’s location.&lt;br&gt;
The &lt;code&gt;fetch&lt;/code&gt; function follows the redirection and gets a response containing &lt;code&gt;Content-Disposition: attachment;&lt;/code&gt; header.&lt;br&gt;
This is the trigger for doing some JS vaudoo to download the file.&lt;br&gt;
That’s sad because we need to use some clever tricks to emulate something that was working natively until that point when the browser was the HTTP client.&lt;/p&gt;
&lt;div class=&quot;admonition experiment&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Experiment&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go to the waiting page, wait a little, then create the file&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Why not hard-code the retry delay?&lt;/p&gt;
&lt;p&gt;The server provides the delay for each retry using the &lt;code&gt;Retry-After&lt;/code&gt; header.&lt;br&gt;
An alternative could be to hardcode the delay in the client.&lt;br&gt;
Using a header provided by the server adds some interesting possibilities, though.&lt;br&gt;
First, it’s our mechanism to tell the client that it still needs to wait.&lt;br&gt;
Without that we would need another way to communicate that information.&lt;br&gt;
Alternatively, we could have a longer hardcoded delay, but that would mean potentially asking the user to wait longer than necessary.&lt;/p&gt;
&lt;p&gt;Secondly, because the server can communicate how long the client should retry, it can change that waiting time.&lt;br&gt;
Maybe the server can predict how long it will take until the file is ready and ask the client to wait just that long.&lt;br&gt;
Or the server is serving many requests at the moment and would like every client to slow down with the amount of requests they send.&lt;br&gt;
In that case, it can increase the delay, using that tool as a back pressure mechanism.&lt;/p&gt;
&lt;/div&gt;
&lt;div class=&quot;admonition help&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Is there a less hacky way to start the download ?&lt;/p&gt;
&lt;p&gt;Hi, JS folks. Is there a better way to start the download of a file when the request is made via &lt;code&gt;fetch&lt;/code&gt;?&lt;br&gt;
This is one of the solutions I found online, but I feel like it should be more straightforward.&lt;/p&gt;
&lt;/div&gt;
&lt;h3 id=&quot;iteration-4%3A-clean-and-get-a-clever-http-client&quot; tabindex=&quot;-1&quot;&gt;Iteration 4: Clean and get a clever HTTP client&lt;/h3&gt;
&lt;div class=&quot;admonition getcode&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Get the code&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout clean-fetch-decorator&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or &lt;a href=&quot;https://github.com/SelrahcD/file-generation-experiment/blob/clean-fetch-decorator/index.js#L50-L99&quot;&gt;view on Github&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;This iteration doesn’t bring any functional improvements, but cleans the code by bringing some functional composition.&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/receipt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;    fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;access&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;filePath&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;constants&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;F_OK&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;err&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// File exists&lt;/span&gt;&lt;br&gt;            res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/download&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;        &lt;span class=&quot;token comment&quot;&gt;// File doesn&#39;t exist yet&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; htmlContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;br&gt;    &amp;lt;!DOCTYPE html&gt;&lt;br&gt;    &amp;lt;html lang=&quot;en&quot;&gt;&lt;br&gt;      &amp;lt;head&gt;&lt;br&gt;        &amp;lt;title&gt;File Download&amp;lt;/title&gt;&lt;br&gt;      &amp;lt;/head&gt;&lt;br&gt;      &amp;lt;body&gt;&lt;br&gt;        &amp;lt;h1&gt;File download&amp;lt;/h1&gt;&lt;br&gt;        &amp;lt;p&gt;We are creating your file.&amp;lt;/p&gt;&lt;br&gt;        &amp;lt;p&gt;Please wait 5 seconds and click the download link below:&amp;lt;/p&gt;&lt;br&gt;        &amp;lt;a href=&quot;/receipt&quot;&gt;Download&amp;lt;/a&gt;&lt;br&gt;      &amp;lt;/body&gt;&lt;br&gt;      &amp;lt;script&gt;&lt;br&gt;&lt;br&gt;      async function fetchWithRetryAfter(fetch, maxRetries = 5) {&lt;br&gt;        let attempt = 0;&lt;br&gt;        let response;&lt;br&gt;    &lt;br&gt;        while (attempt &amp;lt; maxRetries) {&lt;br&gt;            attempt++;&lt;br&gt;            &lt;br&gt;            response = await fetch();&lt;br&gt;            &lt;br&gt;            if(response.headers.get(&#39;Retry-After&#39;)) {&lt;br&gt;              let delayMs = response.headers.get(&#39;Retry-After&#39;) * 1000;&lt;br&gt;              await new Promise(resolve =&gt; setTimeout(resolve, delayMs));&lt;br&gt;              continue;&lt;br&gt;            }&lt;br&gt;            &lt;br&gt;            return response;&lt;br&gt;        }&lt;br&gt;        &lt;br&gt;         return response;&lt;br&gt;    }&lt;br&gt;    &lt;br&gt;        // Function to perform a GET request to the current page&lt;br&gt;       async function fetchDownloadAttachment(fetch) {&lt;br&gt;          const response = await fetch;&lt;br&gt;          &lt;br&gt;          const contentDisposition = response.headers.get(&#39;Content-Disposition&#39;);&lt;br&gt;          &lt;br&gt;          if(contentDisposition &amp;amp;&amp;amp; contentDisposition.includes(&#39;attachment&#39;)) {&lt;br&gt;            // Extract filename (if any) from Content-Disposition&lt;br&gt;            const filenameMatch = contentDisposition.match(/filename=&quot;?(.+)&quot;?/);&lt;br&gt;            const filename = filenameMatch ? filenameMatch[1] : &#39;default-filename&#39;;&lt;br&gt;&lt;br&gt;            // Process the response as a Blob and initiate the download&lt;br&gt;            const data = await response.blob();&lt;br&gt;            const downloadUrl = window.URL.createObjectURL(data);&lt;br&gt;            const a = document.createElement(&#39;a&#39;);&lt;br&gt;            a.href = downloadUrl;&lt;br&gt;            a.download = filename;&lt;br&gt;            document.body.appendChild(a);&lt;br&gt;            a.click();&lt;br&gt;            a.remove();&lt;br&gt;            window.URL.revokeObjectURL(downloadUrl);&lt;br&gt;          }&lt;br&gt;          &lt;br&gt;          return response;&lt;br&gt;      }&lt;br&gt;    &lt;br&gt;    fetchWithRetryAfter(() =&gt; fetchDownloadAttachment(fetch(window.location.href)), 10)&lt;br&gt;        .then(result =&gt; console.log(result))&lt;br&gt;        .catch(error =&gt; console.error(error.message));&lt;br&gt;       &lt;br&gt;      &amp;lt;/script&gt;&lt;br&gt;    &amp;lt;/html&gt;&lt;br&gt;  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;Retry-After&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;        res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;htmlContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The JS code is refactored to have specific functions that are able to interpret an HTTP response and act accordingly.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;fetchWithRetryAfter&lt;/code&gt; looks for a &lt;code&gt;Retry-After&lt;/code&gt; header contained in a HTTP response and performs the retry logic.&lt;br&gt;
&lt;code&gt;fetchDownloadAttachment&lt;/code&gt; looks for an attachment and does the download trick.&lt;/p&gt;
&lt;p&gt;You can create a clever HTTP client by combining all functions and reusing that for all your HTTP requests:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;cleverFetch&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;fetchParams&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;  &lt;span class=&quot;token function&quot;&gt;fetchWithRetryAfter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchDownloadAttachment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fetchParams&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The HTTP response handling logic is now contained in one place, and these functions can be tested independently.&lt;/p&gt;
&lt;h3 id=&quot;iteration-5%3A-spinner&quot; tabindex=&quot;-1&quot;&gt;Iteration 5: Spinner&lt;/h3&gt;
&lt;div class=&quot;admonition getcode&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Get the code&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout spinner&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or &lt;a href=&quot;https://github.com/SelrahcD/file-generation-experiment/blob/spinner/index.js#L9-L115&quot;&gt;view on Github&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;Let’s now move to the modern world of SPA.&lt;br&gt;
In a SPA, you probably wouldn’t want the user to move from the confirmation page to the waiting page if the file isn’t ready but displays a spinner.&lt;/p&gt;
&lt;p&gt;This is what this iteration is about. We want to stay on the confirmation page and avoid moving to the waiting page, even if the file is not there.&lt;/p&gt;
&lt;pre class=&quot;mermaid&quot;&gt;sequenceDiagram
actor User
participant confirmation_page as Confirmation page
participant waiting_page as Waiting page
participant receipt_controller as Receipt controller
participant file_download_controller as File download controller
confirmation_page--&gt;&gt;User: 👁️ Display &quot;Download the file&quot; link
rect rgba(172, 226, 225, 0.4)
User-&gt;&gt;confirmation_page: 👇 Click on &quot;Download the file&quot; [GET]
activate confirmation_page
confirmation_page-&gt;&gt;confirmation_page: 🔄 Display spinner
confirmation_page-&gt;&gt;receipt_controller: GET
loop
    alt File doesn’t exist
        receipt_controller--&gt;&gt;confirmation_page: Retry-After
        confirmation_page-&gt;&gt;confirmation_page: ⏱️ Wait for the delay specified in Retry-After
        confirmation_page-&gt;&gt;receipt_controller: GET
    else File exists
        receipt_controller--&gt;&gt;waiting_page: Content-Disposition: Attachment Location:/download
        confirmation_page-&gt;&gt;file_download_controller: GET
        file_download_controller--&gt;&gt;confirmation_page :  📄 File
        confirmation_page--&gt;&gt;User: 📄 File
    end
end
confirmation_page-&gt;&gt;confirmation_page: Hide spinner
end
&lt;/pre&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;/&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; htmlContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&lt;br&gt;    &amp;lt;!DOCTYPE html&gt;&lt;br&gt;    &amp;lt;html lang=&quot;en&quot;&gt;&lt;br&gt;      &amp;lt;head&gt;&lt;br&gt;        &amp;lt;title&gt;Form submitted&amp;lt;/title&gt;&lt;br&gt;        &amp;lt;style&gt;&lt;br&gt;        .spinner {&lt;br&gt;            display: none;&lt;br&gt;            border: 4px solid rgba(0, 0, 0, 0.1);&lt;br&gt;            width: 16px;&lt;br&gt;            height: 16px;&lt;br&gt;            border-radius: 50%;&lt;br&gt;            border-left-color: #09f;&lt;br&gt;            animation: spin 1s linear infinite;&lt;br&gt;        }&lt;br&gt;&lt;br&gt;        @keyframes spin {&lt;br&gt;            0% {&lt;br&gt;                transform: rotate(0deg);&lt;br&gt;            }&lt;br&gt;            100% {&lt;br&gt;                transform: rotate(360deg);&lt;br&gt;            }&lt;br&gt;        }&lt;br&gt;&lt;br&gt;        .hidden {&lt;br&gt;            display: none;&lt;br&gt;        }&lt;br&gt;    &amp;lt;/style&gt;&lt;br&gt;      &amp;lt;/head&gt;&lt;br&gt;      &amp;lt;body&gt;&lt;br&gt;        &amp;lt;h1&gt;Your form was submitted !&amp;lt;/h1&gt;&lt;br&gt;        &amp;lt;p&gt;Thank you for your information.&amp;lt;/p&gt;&lt;br&gt;        &amp;lt;a href=&quot;/receipt&quot; class=&quot;download&quot;&gt;Get your receipt&amp;lt;div class=&quot;spinner&quot;&gt;&amp;lt;/div&gt;&amp;lt;/a&gt;&lt;br&gt;      &amp;lt;/body&gt;&lt;br&gt;      &amp;lt;script&gt;&lt;br&gt;      &lt;br&gt;      const downloadLinks = document.getElementsByClassName(&#39;download&#39;);&lt;br&gt;      &lt;br&gt;      for(let downloadLink of downloadLinks) {&lt;br&gt;          downloadLink.addEventListener(&#39;click&#39;, function(event) {&lt;br&gt;              event.preventDefault();&lt;br&gt;              &lt;br&gt;              const spinner = event.currentTarget.querySelector(&#39;.spinner&#39;);&lt;br&gt;              spinner.style.display = &#39;inline-block&#39;;&lt;br&gt;              &lt;br&gt;              fetchWithRetryAfter(() =&gt; fetchDownloadAttachment(fetch(this.getAttribute(&#39;href&#39;))), 10)&lt;br&gt;            .then(() =&gt; {&lt;br&gt;                spinner.style.display = &#39;none&#39;&lt;br&gt;            })&lt;br&gt;            .catch(error =&gt; console.error(error))&lt;br&gt;          })&lt;br&gt;      }&lt;br&gt;&lt;br&gt;      async function fetchWithRetryAfter(fetch, maxRetries = 5) {&lt;br&gt;        let attempt = 0;&lt;br&gt;        let response;&lt;br&gt;    &lt;br&gt;        while (attempt &amp;lt; maxRetries) {&lt;br&gt;            attempt++;&lt;br&gt;            &lt;br&gt;            response = await fetch();&lt;br&gt;            &lt;br&gt;            if(response.headers.get(&#39;Retry-After&#39;)) {&lt;br&gt;              let delayMs = response.headers.get(&#39;Retry-After&#39;) * 1000;&lt;br&gt;              await new Promise(resolve =&gt; setTimeout(resolve, delayMs));&lt;br&gt;              continue;&lt;br&gt;            }&lt;br&gt;            &lt;br&gt;            return response;&lt;br&gt;        }&lt;br&gt;        &lt;br&gt;         return response;&lt;br&gt;    }&lt;br&gt;    &lt;br&gt;        // Function to perform a GET request to the current page&lt;br&gt;       async function fetchDownloadAttachment(fetch) {&lt;br&gt;          const response = await fetch;&lt;br&gt;          &lt;br&gt;          const contentDisposition = response.headers.get(&#39;Content-Disposition&#39;);&lt;br&gt;          &lt;br&gt;          if(contentDisposition &amp;amp;&amp;amp; contentDisposition.includes(&#39;attachment&#39;)) {&lt;br&gt;            // Extract filename (if any) from Content-Disposition&lt;br&gt;            const filenameMatch = contentDisposition.match(/filename=&quot;?(.+)&quot;?/);&lt;br&gt;            const filename = filenameMatch ? filenameMatch[1] : &#39;default-filename&#39;;&lt;br&gt;&lt;br&gt;            // Process the response as a Blob and initiate the download&lt;br&gt;            const data = await response.blob();&lt;br&gt;            const downloadUrl = window.URL.createObjectURL(data);&lt;br&gt;            const a = document.createElement(&#39;a&#39;);&lt;br&gt;            a.href = downloadUrl;&lt;br&gt;            a.download = filename;&lt;br&gt;            document.body.appendChild(a);&lt;br&gt;            a.click();&lt;br&gt;            a.remove();&lt;br&gt;            window.URL.revokeObjectURL(downloadUrl);&lt;br&gt;          }&lt;br&gt;          &lt;br&gt;          return response;&lt;br&gt;      }&lt;br&gt;      &lt;br&gt;      &amp;lt;/script&gt;&lt;br&gt;    &amp;lt;/html&gt;&lt;br&gt;  &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;htmlContent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For this, we attach an event listener to the link, prevent it from moving to the next page, display a spinner, and start polling the &lt;code&gt;receipt&lt;/code&gt; route.&lt;br&gt;
We keep the same logic as before and mostly the same JS code.&lt;br&gt;
If the response contains a &lt;code&gt;Retry-After&lt;/code&gt; header, the file is not there yet, and a future request is scheduled.&lt;br&gt;
If the response contains a &lt;code&gt;Content-Disposition: attachment;&lt;/code&gt; header, we do the JS download trick and hide the spinner.&lt;/p&gt;
&lt;p&gt;Also, the URL used for polling is the one specified in the &lt;code&gt;href&lt;/code&gt; attribute of the link.&lt;br&gt;
We didn’t need to change that template part to make it work.&lt;br&gt;
Even better, it means that if JS is disabled, the default behavior will take over from our code and will issue a request to &lt;code&gt;/receipt&lt;/code&gt;, which will display the waiting page or redirect to the file, depending on the file’s existence.&lt;br&gt;
That’s progressive enhancement.&lt;/p&gt;
&lt;div class=&quot;admonition experiment&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Experiment&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;On the confirmation page, click on the link, see the spinner and create the file&lt;/li&gt;
&lt;li&gt;On the confirmation page, create the file, then click on the link&lt;/li&gt;
&lt;li&gt;On the confirmation page, click on the link, and do nothing. (I agree, I should have included an error message here.)&lt;/li&gt;
&lt;li&gt;Disable JS, and try whatever you want to.&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;h3 id=&quot;iteration-6%3A-cleaning&quot; tabindex=&quot;-1&quot;&gt;Iteration 6: Cleaning&lt;/h3&gt;
&lt;div class=&quot;admonition getcode&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Get the code&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout main&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;or &lt;a href=&quot;https://github.com/SelrahcD/file-generation-experiment&quot;&gt;view on Github&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The next iteration is cleaning the code, reducing page duplication, using a templating system.&lt;/p&gt;
&lt;div class=&quot;admonition help&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Help me with Handlebars inlined partials and ExpressJs&lt;/p&gt;
&lt;p&gt;I want to use two regions in the layout, &lt;code&gt;body&lt;/code&gt; and &lt;code&gt;scripts&lt;/code&gt;.&lt;br&gt;
The idea is that every page template is able to provide its content and its own script.&lt;br&gt;
At the moment, the script is pushed right into the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt;.&lt;br&gt;
It works, but I’m curious how to do that with Handlebars and ExpressJs.&lt;br&gt;
I actually spent more time on this issue than on the rest of that demo...&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id=&quot;that-looks-like-a-lot-of-work%2C-should-i-do-that%3F&quot; tabindex=&quot;-1&quot;&gt;That looks like a lot of work, should I do that?&lt;/h2&gt;
&lt;p&gt;Maybe, I don’t know, it depends.&lt;/p&gt;
&lt;p&gt;Maybe your users are fine with waiting for the file to be generated, and you don’t need to do that at all.&lt;br&gt;
As we’ve seen, if you take the asynchronous road, you have multiple stops.&lt;br&gt;
Do you really need to stop at the spinner stop?&lt;/p&gt;
&lt;p&gt;You can start with the first solution, without any JS, and track how many times the file is not available at the moment the user clicks the link to download the file.&lt;br&gt;
If that doesn’t occur very often, because the system is faster at creating the file than user is at clicking the link in the majority of case, that solution may be good enough.&lt;/p&gt;
&lt;p&gt;If this is something common you can start iterating on the solution as we did here.&lt;/p&gt;
&lt;p&gt;Alternatively, you can also try to make file generation faster.&lt;/p&gt;
&lt;h2 id=&quot;alternatives&quot; tabindex=&quot;-1&quot;&gt;Alternatives&lt;/h2&gt;
&lt;p&gt;That solution based on an intermediary status resource is not the only solution to that problem.&lt;/p&gt;
&lt;p&gt;Instead of polling to get the status of the file creation, you could use sockets to push the information to the client.&lt;br&gt;
If you’re working in a server-to-server mode, you could also use a webhook mechanism to notify the other server that the file is ready.&lt;/p&gt;
&lt;h2 id=&quot;resources&quot; tabindex=&quot;-1&quot;&gt;Resources&lt;/h2&gt;
&lt;p&gt;Derek Comartin from the Code Opinion YouTube channel, made a nice video on that subject:&lt;br&gt;
&lt;a href=&quot;https://www.youtube.com/watch?v=2yUnY61zdAk&quot;&gt;📺 Avoiding long running HTTP API requests. - Code Opinion&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &amp;quot;RESTful Web Services Cookbook&amp;quot; covers the solution of the status resource as well.&lt;/p&gt;
&lt;p&gt;So, here we are.&lt;br&gt;
We are now able to display a confirmation page even when a file is still being generated.&lt;br&gt;
This improves users experience as they don’t have to wait.&lt;br&gt;
As we’ve seen, the solution, in its simplest form, doesn’t require a lot of code.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Embed external code via HTTP on an eleventy blog</title>
      <link href="https://blog.charlesdesneuf.com/articles/embed-code-from-external-source-via-HTTP-with-11ty/"/>
      <updated>2024-05-27T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/embed-code-from-external-source-via-HTTP-with-11ty/</id>
      <summary>Do you want to avoid copy-pasting code inside your 11ty blog? Fetch it directly from an external resource via HTTP!</summary>
      <content type="html">&lt;p&gt;When I was writing my last article about &lt;a href=&quot;/articles/generating-a-file-without-blocking-form-submission/&quot;&gt;generating a file without blocking form submission&lt;/a&gt;, I wanted to embed some code samples to explain the solution, but I wanted to avoid copy-pasting the code from the demo repository to the blog article. Inspired by Asciidoc partial inclusion, which I described in &lt;a href=&quot;/articles/tester-les-extraits-de-code-dans-un-livre/&quot;&gt;a previous article&lt;/a&gt; (in French), I decided to implement a similar method to retrieve some code via HTTP directly from a GitHub repo.&lt;/p&gt;
&lt;p&gt;Well, I say &amp;quot;implement&amp;quot;... I should probably say &amp;quot;ask Chat GPT to do that for me.&amp;quot; I wanted to see if the LLM could save me some time in implementing the solution. I thought the solution would be relatively easy to describe and create. I even created the prompt on my phone while commuting, just after having the idea. I tasked ChatGPT to build an Eleventy shortcode that takes a URL and a tag name, fetches the content, and returns the content between the start and end tags.&lt;/p&gt;
&lt;p&gt;I had previous experiences where it took more time doing it with Chat GPT’s help than without it, but this time it wasn’t the case. Success on the first try, the generated code looked like what I imagined.&lt;/p&gt;
&lt;p&gt;I tried the produced code when I was back on my laptop, and it worked. I just edited it a bit.&lt;/p&gt;
&lt;p&gt;Here is the piece of code that you can include to configure 11ty in your &lt;code&gt;.eleventy.js&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;language-js&quot;&gt;&lt;code class=&quot;language-js&quot;&gt; eleventyConfig&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;addAsyncShortcode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;externalContent&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;url&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; tag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// Fetch the content from the provided URL&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;text&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// Define the start and end tags based on the input tag&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; startTag &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;// tag::&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;tag&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; endTag &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;// end::&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;tag&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;[]&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// Extract the content between the tags&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; startIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;startTag&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; startTag&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; endIndex &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;indexOf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;endTag&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;startIndex &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; endIndex &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;Tag &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;tag&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; not found in the document at &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;url&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// Return the substring between start and end indices&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;substring&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;startIndex&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; endIndex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And here is how you can use it with the new externalContent shortcode:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;br&gt;{% externalContent &quot;https://gist.githubusercontent.com/SelrahcD/a0d1e2295f4195c51836e36b86b8232b/raw/a4d44b9ad70ca9595f149541b8d69a47574d64cf/somePHP.php&quot;, &quot;MyCode&quot; %}&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;// Code that is inside the MyCode tag block&lt;br&gt;
$a += $a;&lt;/p&gt;
&lt;p&gt;Which fetches &lt;a href=&quot;https://gist.githubusercontent.com/SelrahcD/a0d1e2295f4195c51836e36b86b8232b/raw/a4d44b9ad70ca9595f149541b8d69a47574d64cf/somePHP.php&quot;&gt;the raw content&lt;/a&gt; for a &lt;a href=&quot;https://gist.github.com/SelrahcD/a0d1e2295f4195c51836e36b86b8232b&quot;&gt;file in a Gist&lt;/a&gt; and displays:&lt;/p&gt;
&lt;p&gt;// Code that is inside the MyCode tag block&lt;br&gt;
$a += $a;&lt;/p&gt;
&lt;p&gt;You can even use that shortcode inside a code block:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token code&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;```&lt;/span&gt;&lt;span class=&quot;token code-language&quot;&gt;php&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token code-block language-php&quot;&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt; externalContent &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;https://gist.githubusercontent.com/SelrahcD/a0d1e2295f4195c51836e36b86b8232b/raw/a4d44b9ad70ca9595f149541b8d69a47574d64cf/somePHP.php&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;MyCode&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;%&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;```&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To get a nicely displayed PHP Code&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Code that is inside the MyCode tag block&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s a very raw solution and far from being quite as good as the Asciidoc implementation.&lt;br&gt;
It deals only with comments starting with &lt;code&gt;//&lt;/code&gt; and doesn’t handle tags inside tags.&lt;br&gt;
But it’s good enough for my needs, and sometimes that just we don’t need to work more than that.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Getting your secret password from 1Password in a script</title>
      <link href="https://blog.charlesdesneuf.com/articles/getting-your-secret-password-from-1password-in-a-script/"/>
      <updated>2024-05-29T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/getting-your-secret-password-from-1password-in-a-script/</id>
      <summary>Learn how to securely access passwords and secrets in your shell scripts using 1Password CLI. Avoid hardcoding sensitive data and enable easy credential rotation without script modifications.</summary>
      <content type="html">&lt;p&gt;A few weeks ago, I need to create a script that required one of my password.&lt;br&gt;
I didn’t want to hardcode my password in the script and decided to use one super nice functionality from 1Password : access secrets stored in vaults using 1Password CLI, via the &lt;code&gt;op&lt;/code&gt; command.&lt;/p&gt;
&lt;h2 id=&quot;steps&quot; tabindex=&quot;-1&quot;&gt;Steps&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;First, you’ll need to install &lt;a href=&quot;https://developer.1password.com/docs/cli/get-started/&quot;&gt;1Password CLI&lt;/a&gt; that will give you the ability to talk to 1Password right from your scripts.&lt;/li&gt;
&lt;li&gt;Then, open the 1Password application, go to the item where your secret is stored. For instance, here I needed my Gmail password. &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; &lt;img src=&quot;./images/1password-secret.png&quot; alt=&quot;&quot;&gt;&lt;/li&gt;
&lt;li&gt;Once copied, go to you script and get the secret using the &lt;code&gt;op&lt;/code&gt; command.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;gmail_pass&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;op&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;read&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;PASTE THE SECRET REFERENCE HERE&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Use that secret in your script.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now, when you’ll run your script, 1Password will ask you to identify and will give access to the secret to your script.&lt;/p&gt;
&lt;p&gt;This as a couple of advantages.&lt;br&gt;
The obvious one is that you don’t need to hardcode sensitive information in your scripts.&lt;br&gt;
Secondly, that means its easier to rotate password because you don’t need to edit your scripts when you change your password, and you can do this easily even if your in a team setup.&lt;/p&gt;
&lt;p&gt;A nice little trick!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Actually, you can’t really use your Gmail password for scripts but will need an application password. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>How I divided a PhpUnit test suite execution by 5</title>
      <link href="https://blog.charlesdesneuf.com/articles/how-i-divided-a-phpunit-test-suite-execution-by-5/"/>
      <updated>2024-06-07T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/how-i-divided-a-phpunit-test-suite-execution-by-5/</id>
      <summary>Learn how I reduced PHPUnit test execution from 20 to 4 minutes using Paratest parallelization and database optimization in a Symfony legacy application.</summary>
      <content type="html">&lt;p&gt;During my last mission, I was working with a team that invested in testing to secure a Symfony legacy application and find ways to prevent some of the regressions. This was a really good move.&lt;/p&gt;
&lt;p&gt;They mainly had two types of tests:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Some tests were written with CucumberJs, automating real user workflows on the user interface via Puppeteer. Execution of these tests involved a lot of HTTP requests, database calls, code logic execution, and wait time for rendering.&lt;/li&gt;
&lt;li&gt;Some tests used PhpUnit, written with Symfony test clients, going all the way down to the database and back again.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These two test suites were slow. The first one took around 50 minutes on the build machine. The team decided to avoid running it in the CI on every push and ran them only at merge time. The second one took a little more than 20 minutes. Suffice it to say that this was too long to run the tests after every code change.&lt;/p&gt;
&lt;p&gt;The company decided to invest a few days of my presence to speed up the tests. I worked on the PhpUnit test suite and managed to reduce the time by a factor of 5.&lt;/p&gt;
&lt;p&gt;Having an impact on the entire test suite meant looking for a global solution and avoiding trying to edit every test one by one to make them faster. That would just take too long.&lt;/p&gt;
&lt;h1 id=&quot;parallelization-with-paratest&quot; tabindex=&quot;-1&quot;&gt;Parallelization with Paratest&lt;/h1&gt;
&lt;p&gt;So I went with parallelization. Instead of running each test one after the other, let’s run multiple tests at the same time, taking advantage of the multiple processors of our computers.&lt;/p&gt;
&lt;p&gt;In PHP, we can use &lt;a href=&quot;https://github.com/paratestphp/paratest&quot;&gt;Paratest&lt;/a&gt;, a tool that will run multiple tests from PhpUnit at the same time.&lt;/p&gt;
&lt;p&gt;The first step was to install Paratest with &lt;code&gt;composer require --dev brianium/paratest&lt;/code&gt;. Then, run it with &lt;code&gt;vendor/bin/paratest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It worked; tests were running in parallel, but this was just a first step because a lot of tests started to fail.&lt;/p&gt;
&lt;p&gt;The vast majority of tests were tied to the database, which is acting as a shared resource. This was the main cause of the new failures. Because the database is shared and multiple tests are running in parallel, they interact with each other via the database. Imagine that test A is trying to read some data from the database while test B deletes that data. If A passes before B, the test will pass, but if B passes before A, test A will fail.&lt;/p&gt;
&lt;p&gt;I&#39;ve explored multiple solutions to the problem of the shared database for parallel tests in a &lt;a href=&quot;/articles/tests-en-parallele-et-base-de-donnees/&quot;&gt;past article&lt;/a&gt; (in French). Some of them were not applicable in our case.&lt;/p&gt;
&lt;p&gt;Removing the dependency on the database during testing would require too much work because the code had a tight coupling with the database via heavy usage of an ORM. Using a fake for the database wasn&#39;t possible.&lt;/p&gt;
&lt;p&gt;Decorating each test with a transaction wasn&#39;t possible either because the framework was tweaked and used multiple connections to the database side-by-side.&lt;/p&gt;
&lt;p&gt;The solution left was to use one database for each process. Each one of Paratest&#39;s runners will have its own database, which will prevent tests from interacting via the database.&lt;/p&gt;
&lt;p&gt;We will return to the creation of a database for each test process later, but for now, let&#39;s focus on another issue: how can the application know which database to connect to?&lt;/p&gt;
&lt;h1 id=&quot;connecting-to-the-right-database&quot; tabindex=&quot;-1&quot;&gt;Connecting to the right database&lt;/h1&gt;
&lt;p&gt;When Paratest starts a new process, it injects an environment variable named &lt;code&gt;TEST_TOKEN&lt;/code&gt; which contains an identifier for the process. This is just an integer, starting from 1 up to the number of started processes.&lt;/p&gt;
&lt;p&gt;Let&#39;s say that test databases are created using the original test database&#39;s name suffixed with the test token. For instance, if the original test database name is &lt;code&gt;test_db&lt;/code&gt;, the first process database is named &lt;code&gt;test_db_1&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The database name is passed to the application via an environment variable &lt;code&gt;DATABASE_NAME&lt;/code&gt;. It&#39;s now a matter of combining the information coming from the two environment variables, &lt;code&gt;DATABASE_NAME&lt;/code&gt; and &lt;code&gt;TEST_TOKEN&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To do this, I created a &lt;a href=&quot;https://symfony.com/doc/current/configuration/env_var_processors.html&quot;&gt;Symfony Environment Variable Processor&lt;/a&gt;, which allows us to manipulate the environment variable used in parameters.&lt;/p&gt;
&lt;p&gt;In the application config, a &lt;code&gt;database_name&lt;/code&gt; parameter exists and is reused for every service definition in need of knowing which database to connect to. To read the &lt;code&gt;DATABASE_NAME&lt;/code&gt; environment variable, it was originally defined as:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token key atrule&quot;&gt;database_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;%env(DATABASE_NAME)%&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I changed it to:&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;parameters&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token key atrule&quot;&gt;database_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;%env(testdb:DATABASE_NAME)%&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The addition of &lt;code&gt;testdb:&lt;/code&gt; indicates to the Symfony container to use the &lt;code&gt;TestDBEnvVarProcessor&lt;/code&gt; to process the environment variable.&lt;/p&gt;
&lt;p&gt;Here is the environment variable processor class:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name-definition class-name&quot;&gt;TestDBEnvVarProcessor&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EnvVarProcessorInterface&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getEnv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$prefix&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name class-name-fully-qualified type-declaration&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;Closure&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$getEnv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$env&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$getEnv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token variable&quot;&gt;$testToken&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$getenv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;TEST_TOKEN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$testToken&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$env&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;_&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$testToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;getProvidedTypes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;  &lt;br&gt;            &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;testdb&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;string&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;br&gt;        &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;getEnv&lt;/code&gt; method is there to transform the original environment variable to the test database name.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;$getEnv($name)&lt;/code&gt; gives us the value of &lt;code&gt;DATABASE_NAME&lt;/code&gt;, &lt;code&gt;test_db&lt;/code&gt; in our case.&lt;/p&gt;
&lt;p&gt;Then, we try to see if a &lt;code&gt;TEST_TOKEN&lt;/code&gt; environment variable is defined. If it&#39;s not the case, we keep the database name as it is. If we have a &lt;code&gt;TEST_TOKEN&lt;/code&gt;, we concatenate the original name with the token.&lt;/p&gt;
&lt;p&gt;And we need to tell the framework where to look for the environment variable processor with a new service declaration.&lt;/p&gt;
&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token key atrule&quot;&gt;XXX&#92;YYY&#92;TestDBEnvVarProcessor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;  &lt;br&gt;	    &lt;span class=&quot;token key atrule&quot;&gt;tags&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;container.env_var_processor&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The process-scoped database will now be used when running with Paratest, and the original test database will keep being used if tests are run with PhpUnit.&lt;/p&gt;
&lt;h1 id=&quot;a-simpler-and-better-way-to-achieve-this&quot; tabindex=&quot;-1&quot;&gt;A simpler and better way to achieve this&lt;/h1&gt;
&lt;p&gt;While I was writing this article, it occurred to me that a simpler solution to this problem exists: we can rewrite the &lt;code&gt;DATABASE_NAME&lt;/code&gt; environment variable right in the PhpUnit bootstrap file.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$testToken&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;TEST_TOKEN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$testToken&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$db&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;DB_NAME&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$testDB&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$db&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;_&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$testToken&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;putenv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;DB_NAME=${testDB}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I don&#39;t know why I didn&#39;t use that solution at that time. Maybe I tried it, missed something, failed, and found the other solution or just didn&#39;t think about it.&lt;/p&gt;
&lt;p&gt;Anyway, I think this is a better solution for multiple reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It&#39;s less and simpler code.&lt;/li&gt;
&lt;li&gt;It doesn&#39;t require modifying the application; the environment variable modification is made outside of it. This is better because the application should just take an environment variable and trust that it was provided with the right information.&lt;/li&gt;
&lt;li&gt;Because the environment variable modification code is written in the test framework bootstrap, it is run only in test mode. Only when it&#39;s needed and no risk of issues in production.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have to choose between the two solutions, I think you should pick this one. I included the other solution in the article because this is how I actually did it for that project and to share the existence of Symfony&#39;s Env Var Processors. Using them for this use case looked like a good idea but maybe isn&#39;t after all.&lt;/p&gt;
&lt;h1 id=&quot;creating-the-test-databases&quot; tabindex=&quot;-1&quot;&gt;Creating the test databases&lt;/h1&gt;
&lt;p&gt;So far, the only test database was created using a migration script and some data seeders during PhpUnit bootstrap.&lt;/p&gt;
&lt;p&gt;We could reuse that data setup mechanism for every database. The only issue is that it was slow. When you launched the tests you had to wait for a little while - I don&#39;t recall exactly but I would say around 1 minute - before the tests started to be executed.&lt;/p&gt;
&lt;p&gt;We wanted to avoid each process from waiting for its own migration to be made, and I found a solution to that problem.&lt;/p&gt;
&lt;p&gt;But first, a little detour.&lt;/p&gt;
&lt;h2 id=&quot;paratest%2C-phpunit%2C-and-the-bootstrap-file&quot; tabindex=&quot;-1&quot;&gt;Paratest, PhpUnit, and the bootstrap file&lt;/h2&gt;
&lt;p&gt;When tests are run with Paratest, the PhpUnit bootstrap is called multiple times:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Once before starting Paratest processes&lt;/li&gt;
&lt;li&gt;Once per process&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can see that by adding this line to your PhpUnit bootstrap:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token function&quot;&gt;file_put_contents&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;__DIR__&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;/log.txt&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;In phpunit bootstrap.php ${testToken} &#92;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;FILE_APPEND&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After running Paratest with two processors, using &lt;code&gt;./vendor/bin/paratest -p 2&lt;/code&gt;, and 3 test classes, you&#39;ll see the following in &lt;code&gt;log.txt&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;In phpunit bootstrap.php    &lt;br&gt;In phpunit bootstrap.php 1   &lt;br&gt;In phpunit bootstrap.php 2&lt;br&gt;In phpunit bootstrap.php 1&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It took me a while to discover that. I was facing weird behaviors that I didn&#39;t understand. I tried adding some &lt;code&gt;echo&lt;/code&gt; statements, but because they were in other processes, I couldn&#39;t see them. I tried debugging, which is a super fun thing to do because you need to pass the debug settings to processes started by Paratest&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Why was this detour interesting?&lt;/p&gt;
&lt;p&gt;We&#39;ve learned that the bootstrap file is run once per process, which means we can set up each database&#39;s data in its own process. This has a couple of advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We don&#39;t need to know how many processes Paratest will use prior to creating the databases. We will always create the right number of databases.&lt;/li&gt;
&lt;li&gt;The setup for each database is made in parallel, which means faster setup than a sequential one.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, we&#39;ve learned that the bootstrap file is called once per test class. We can see that thanks to the repeated &lt;code&gt;In phpunit bootstrap.php 1&lt;/code&gt; line.&lt;/p&gt;
&lt;p&gt;PhpUnit&#39;s bootstrap seemed to be a good place to create the databases for each processor. Even better, because we were going through the bootstrap for each test class, we could have a new database for each one of them. This is great because I was also on a side quest of removing test flakiness due to coupling via the shared resource that is the database. But, first, let&#39;s create multiple databases.&lt;/p&gt;
&lt;h2 id=&quot;creating-multiple-test-databases&quot; tabindex=&quot;-1&quot;&gt;Creating multiple test databases&lt;/h2&gt;
&lt;p&gt;Below the pseudo code of my original try at creating all the databases:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;testDBName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword return-type&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;test_db_&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;TEST_TOKEN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;TEST_TOKEN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Migrate and seed with original database name&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Migrate and seed with testDbName()&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we had no &lt;code&gt;TEST_TOKEN&lt;/code&gt; set, either because we were not using Paratest or because we were in the first bootstrap, we run the migration and seeding as we did before. If the &lt;code&gt;TEST_TOKEN&lt;/code&gt; was specified, migrate and seed the specific database.&lt;/p&gt;
&lt;p&gt;This solution comes with a couple of issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Even when we are using Paratest, we are creating the original database we won&#39;t need during the test execution. This is a loss of time.&lt;/li&gt;
&lt;li&gt;Worse, because the migration and seeding step is slow, the test suite execution time is now longer than it was before as that step is repeated for each and every test class. Running that in parallel doesn&#39;t compensate for that at all.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;from-slow-to-fast...&quot; tabindex=&quot;-1&quot;&gt;From slow to fast...&lt;/h2&gt;
&lt;p&gt;So here we are, with tests running in parallel and a test suite slower than it was before because of database seeding.&lt;/p&gt;
&lt;p&gt;By chance, the other test suite, the one running on CucumberJs, offered me a solution. To improve the setup of tests for that test suite, the team had already tried an idea to improve database setup time.&lt;/p&gt;
&lt;p&gt;Instead of running migration and data creation for each test, they created a dump of the database and reused it for every test. I could totally steal that idea and reuse it to speed up the PhpUnit test suite as well.&lt;/p&gt;
&lt;p&gt;I changed the PhpUnit bootstrap file to migrate and seed the database and dump its content to a file when the &lt;code&gt;TEST_TOKEN&lt;/code&gt; environment variable is missing. This ensures that we always have an up-to-date database including the latest schema and data before running the test. When the &lt;code&gt;TEST_TOKEN&lt;/code&gt; environment variable is set, instead of going through the original database process, I used the dump to populate the test process dedicated database.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getenv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;TEST_TOKEN&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token constant boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Migrate and seed with original database name&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Dump the content of the database&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token variable&quot;&gt;$dumpCommand&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;PGPASSWORD=&#39;${POSTGRES_PASSWORD}&#39; pg_dump --no-owner -h ${POSTGRES_HOST} -U ${POSTGRES_USER} -d ${POSTGRES_DB} -Fc &gt; ${DUMP_FILE}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$dumpCommand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token variable&quot;&gt;$testDbName&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;testDBName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Drop the database if it exists, this prevents error when trying to create an already existing DB&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token variable&quot;&gt;$createDabataseCommand&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;PGPASSWORD=&#39;${POSTGRES_PASSWORD}&#39; psql -h ${POSTGRES_HOST} -U ${POSTGRES_USER} -c &#39;DROP DATABASE IF EXISTS ${testDbName}&#39;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$createDabataseCommand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Create the database&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token variable&quot;&gt;$createDabataseCommand&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;PGPASSWORD=&#39;${POSTGRES_PASSWORD}&#39; psql -h ${POSTGRES_HOST} -U ${POSTGRES_USER} -c &#39;CREATE DATABASE ${testDbName}&#39;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$createDabataseCommand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Restore the dump&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token variable&quot;&gt;$restoreDumpCommand&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;PGPASSWORD=&#39;${POSTGRES_PASSWORD}&#39; pg_restore -h ${POSTGRES_HOST} -U ${POSTGRES_USER} -d ${testDbName} ${DUMP_FILE}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$restoreDumpCommand&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The dump restoration trick combined with the parallel run reduced the test duration by a factor of 5. Still a bit slow to my taste to be able to TDD, but a vast improvement nevertheless. Running tests on local machines multiple times per hour started to be possible.&lt;/p&gt;
&lt;p&gt;As you can see, while some ideas are applicable to multiple codebases, this is purely an ad-hoc solution. The solution was created with a lot of trial and error, sweat, hurray moments, and painful setbacks.&lt;/p&gt;
&lt;p&gt;The point here is that a super slow test suite is something you can escape. You probably won&#39;t have a super fast test suite without a lot of test rework if your tests are all making database calls, but you can improve your daily life up to some point. The next steps are to decouple business logic code from infrastructure, use unit tests for the former and integration tests for the latter. This is more work and depending on the state of your codebase can be a difficult thing to do. Sometimes it can be helpful to get some assistance to go through these kinds of challenges. If you feel like your codebase or your test suite could benefit from some rework to make your life easier and speed up your delivery, &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let’s chat&lt;/a&gt;!&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;According to my shell history, you probably can do this via Paratest &lt;code&gt;--passthru-php&lt;/code&gt; option. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Tags, the Forgotten Feature of Test Runners</title>
      <link href="https://blog.charlesdesneuf.com/articles/tags-the-forgotten-feature-of-test-runners/"/>
      <updated>2024-07-23T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/tags-the-forgotten-feature-of-test-runners/</id>
      <summary>Some examples of using tags that will change your view of testing.</summary>
      <content type="html">&lt;p&gt;&lt;em&gt;Cet article est disponible &lt;a href=&quot;/articles/tags-fonctionnalite-oubliee-des-lanceurs-de-tests/&quot;&gt;en français&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;There is a feature of test runners that we use far too little, myself included: tags.&lt;/p&gt;
&lt;p&gt;With a bit of imagination, they can provide great services, and even change some discussions around testing and test cases organization.&lt;/p&gt;
&lt;h2 id=&quot;what-are-tags%3F&quot; tabindex=&quot;-1&quot;&gt;What are tags?&lt;/h2&gt;
&lt;p&gt;Usually, to create groups of tests, we rely on the position of the test within our test suite. Tests are grouped because they belong to the same test class, the same &lt;code&gt;describe&lt;/code&gt; method, the same file, or the same file hierarchy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tags are another way to create groups of tests without needing to know where they are stored.&lt;/strong&gt; Some tools even refer to them as groups rather than tags.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./annotation.png&quot; alt=&quot;A test with an @group database annotation&quot;&gt;&lt;/p&gt;
&lt;p&gt;Generally, tags are implemented through the annotation mechanism of the language in which the tests are written.&lt;/p&gt;
&lt;p&gt;Once tags are in place, we have a new way to decide which tests we want to run or not. We will see examples of usage in the rest of the article.&lt;/p&gt;
&lt;h3 id=&quot;my-test-runner-doesn%E2%80%99t-support-tags&quot; tabindex=&quot;-1&quot;&gt;My test runner doesn’t support tags&lt;/h3&gt;
&lt;p&gt;That’s unfortunate...&lt;/p&gt;
&lt;p&gt;But chances are, the community offers a plugin that provides a similar mechanism&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;If not, all is not lost. There is a good chance the test runner allows filtering tests based on their name&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. &lt;strong&gt;We can then decide to include tags at the end of test names and filter on the pattern that interests us.&lt;/strong&gt; Adding a tag to the test name adds noise, information that is not relevant in some cases, so it’s important to have a convention that makes it easy to know if information is a tag or not. Starting all tags with a hashtag (#) is probably a good idea.&lt;/p&gt;
&lt;h2 id=&quot;usage-examples&quot; tabindex=&quot;-1&quot;&gt;Usage examples&lt;/h2&gt;
&lt;p&gt;Let’s look at some ideas of what tags can do for us.&lt;/p&gt;
&lt;h3 id=&quot;test-type&quot; tabindex=&quot;-1&quot;&gt;Test type&lt;/h3&gt;
&lt;p&gt;The first idea is the most obvious, so obvious that it may seem unnecessary, yet...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tags can indicate the type of the test.&lt;/strong&gt; Is it a unit test, an integration test, an end-to-end test, an acceptance test, or another type of test?&lt;/p&gt;
&lt;p&gt;With tags, we can then decide to run only certain types of tests at certain times.&lt;/p&gt;
&lt;p&gt;This idea seems unnecessary because you may already be using test suites arranged in different folders to separate test types, and you can already decide which types of tests to run.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The downside of sorting test types by folder is that it forces us to maintain one or more file hierarchies mirroring that of the production code.&lt;/strong&gt; And let’s be honest, most of the time this synchronization is not done. We end up with completely different folder hierarchies everywhere.&lt;/p&gt;
&lt;p&gt;Thanks to tags, we can abandon this constraint and place all tests in the same hierarchy, greatly facilitating maintainability.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We can even consider going further and placing tests directly next to the code and still have a way to choose which types of tests to run.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And now, no more synchronized folder hierarchies to maintain, yet we can easily select which types of tests to run.&lt;/p&gt;
&lt;h3 id=&quot;dependencies&quot; tabindex=&quot;-1&quot;&gt;Dependencies&lt;/h3&gt;
&lt;p&gt;The second idea is a continuation of the previous one.&lt;/p&gt;
&lt;p&gt;But first, let me ask you a few questions:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How many times have you had a debate about whether a test is a unit test?&lt;/strong&gt; Or rather an integration test? Or perhaps an acceptance test? Maybe an end-to-end test?&lt;br&gt;
How much time have you wasted arguing as a team about how to classify a test and which folder to put it in?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What if the most important thing is not the type of test but its characteristics?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Think about it for 2 minutes: &lt;strong&gt;why do we care so much about the type of a test?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Isn’t being able to separate slow tests from others a big part of it?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If your workflow relies heavily on tests, for example if you practice TDD, you probably don’t want to wait a long time to find out if your changes had an unexpected impact on the system. Chances are you frequently run the fastest tests as a first safety net, and occasionally run all the tests, even the slowest ones, for a more comprehensive safety net.&lt;/p&gt;
&lt;p&gt;So rather than debating the type of tests, let’s ask ourselves why they are slow. Generally, tests are slow when they involve I/O operations. Tests that touch a database, a file system, or make calls to external services via the network are much slower than other tests&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;We then have a new idea for tag categories: tags that indicate dependencies.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;When a test needs a particular dependency, we can indicate it with a tag, such as &lt;code&gt;database&lt;/code&gt;, &lt;code&gt;external-call&lt;/code&gt;, &lt;code&gt;network&lt;/code&gt;, &lt;code&gt;filesystem&lt;/code&gt;, etc.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With this type of tag &lt;strong&gt;we no longer have to try to precisely categorize tests; we just describe the dependencies they need, avoiding endless debates.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We can now select or exclude certain tests based on what they depend on. When we need to run only the fastest tests, it’s easy to exclude troublesome dependencies.&lt;/p&gt;
&lt;p&gt;As a bonus, if a dependency is missing in an environment, for example if we can’t make calls to external services because we’re on a train, we can easily exclude all tests that won’t pass anyway.&lt;/p&gt;
&lt;p&gt;This idea of selection by dependency, which is unmanageable with the file system, becomes possible with tags.&lt;/p&gt;
&lt;h3 id=&quot;more-precise-documentation&quot; tabindex=&quot;-1&quot;&gt;More precise documentation&lt;/h3&gt;
&lt;p&gt;I often talk about &lt;a href=&quot;/articles/extra-readable-tests/&quot;&gt;using tests as documentation&lt;/a&gt;, for developers, but also for the rest of the team, and even for the business.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./documentation.png&quot; alt=&quot;Documentation generated via phpunit --testdox.&quot;&gt;&lt;/p&gt;
&lt;p&gt;In PHP, with PHPUnit, the &#39;--testdox&#39; option allows generating documentation in text or HTML format, except...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Different people are interested in different aspects of the system. Rather than generating huge documents that no one will read, it is possible to add tags for each topic of interest.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With these tags, we can now generate specific documentation for everyone:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;documentation about payment features by filtering on the &lt;code&gt;feat-payment&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;documentation on the backoffice operation with &lt;code&gt;tool-bo&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Again, we can manage with the file system, but &lt;strong&gt;there are also more transversal topics&lt;/strong&gt;, such as security (&lt;code&gt;security&lt;/code&gt;), translation (&lt;code&gt;i18n&lt;/code&gt;), or business rules that concern only customers from certain countries (&lt;code&gt;market-es&lt;/code&gt;)...&lt;/p&gt;
&lt;p&gt;By using tags, we can mark topics that interest us on a recurring basis.&lt;/p&gt;
&lt;h3 id=&quot;link-with-the-ticketing-system&quot; tabindex=&quot;-1&quot;&gt;Link with the ticketing system&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Tags can also link to a ticketing system.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With the ticket number as a tag, we can easily find a non-regression test for a bug &lt;code&gt;bug-3457&lt;/code&gt; or find all tests related to a user story &lt;code&gt;US-89&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I don’t think these tags are very useful in the long term because after a while knowing that a behavior was created in connection with a ticket is no longer very relevant in my opinion, but they can still be useful in the short term.&lt;/p&gt;
&lt;p&gt;For example, the build tool could generate specific documentation based on the tests for each user story and display it directly in a pull request comment&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt; or check that a non-regression test has been added before resolving the bug.&lt;/p&gt;
&lt;h2 id=&quot;nomenclature&quot; tabindex=&quot;-1&quot;&gt;Nomenclature&lt;/h2&gt;
&lt;p&gt;As with any tool, overuse can have unpleasant consequences.&lt;/p&gt;
&lt;p&gt;To prevent the use of tags from becoming a huge mess &lt;strong&gt;it is important to think about a nomenclature&lt;/strong&gt;. To do this, ask yourself what you can get from using tags, whether to select tests to run or to extract information from them.&lt;/p&gt;
&lt;p&gt;Once you have defined what tags will be used for and identified categories &lt;strong&gt;you can use prefixes to easily identify tags&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We encountered some ideas in this article:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;feat-payment&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;US-89&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bug-3457&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;tool-backoffice&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;market-es&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;domain-delivery&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, remember to clean up occasionally and remove tags that are no longer useful - especially if you use tags linked to the ticketing system.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./tag-list.png&quot; alt=&quot;List tags with phpunit --list-groups&quot;&gt;&lt;/p&gt;
&lt;p&gt;For this, let your test runner help you. If it allows filtering tests by group, it probably has a command to list tags. For example, PHPUnit has a &lt;code&gt;--list-groups&lt;/code&gt; command. If the test runner doesn’t help, you can probably manage with &lt;code&gt;grep&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Some tags will have more or less long lifespans by their nature, your usage will likely change as well, and that’s okay.&lt;/p&gt;
&lt;p&gt;I hope this article has given you ideas, whether the ones presented or others, on how you can incorporate the use of tags into your daily routine to get the most out of your tests.&lt;/p&gt;
&lt;p&gt;And if you want to make sure you have tests you truly enjoy working with, you can access &lt;a href=&quot;https://formation.charlesdesneuf.com/ameliorez-vos-tests-automatises&quot;&gt;my video training on improving automated tests&lt;/a&gt;, in French. You can also &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;schedule a meeting&lt;/a&gt; so we can work together to take your team to the next level.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;For example, &lt;a href=&quot;https://www.npmjs.com/package/jest-runner-groups&quot;&gt;jest-runner-groups&lt;/a&gt; if you use Jest. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://jestjs.io/docs/cli#--testnamepatternregex&quot;&gt;Jest’s testnamepatternregex option&lt;/a&gt; can help. &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Dare I simply say that these tests are much slower than unit tests? &lt;a href=&quot;#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;If your workflow uses PRs, of course. &lt;a href=&quot;#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>3 and a half heuristics to avoid putting business logic in the wrong place when doing event sourcing</title>
      <link href="https://blog.charlesdesneuf.com/articles/3-and-a-half-heuristics-to-avoid-putting-business-logic-in-the-wrong-place-when-doing-event-sourcing/"/>
      <updated>2024-08-09T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/3-and-a-half-heuristics-to-avoid-putting-business-logic-in-the-wrong-place-when-doing-event-sourcing/</id>
      <summary>How to avoid the shooting you in the foot by putting logic in the wrong place in your event-sourced system and not recording the right information?</summary>
      <content type="html">&lt;p&gt;One mistake that is pretty common to people new to event sourcing is putting business logic in the code responsible for rebuilding the state that will serve to make a decision &lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. Any decent introduction to event sourcing will warn people against that, but still, this is a common mistake.&lt;/p&gt;
&lt;p&gt;The issue here is that you still need some logic to rebuild the state, and it’s easy to confuse that logic with business logic. We tend to fall into the trap of confusing the two when the computation seems stable and doesn’t change often. After all, why should we put that piece of information in an event when we can recompute it during the rebuild?&lt;/p&gt;
&lt;p&gt;I want to offer three ideas that can help distinguishing business logic from state rebuilding logic.&lt;/p&gt;
&lt;h2 id=&quot;did-a-business-expert-tell-you-something-about-that-piece-of-logic%3F&quot; tabindex=&quot;-1&quot;&gt;Did a business expert tell you something about that piece of logic?&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Is the code that you’re about to write interesting for a business expert? Is this something that you discussed with them? Did they tell you something about that specific rule / number / ... ?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In that case, this is business logic, and it should go in the command part of your system, and the results of any calculation should be stored in an event.&lt;/p&gt;
&lt;p&gt;Let’s take two examples. The first one is a common one, the second one comes from a team I’m working with.&lt;/p&gt;
&lt;p&gt;The first one concerns tax rates. For now, to get the price with all taxes included, you need to multiply every price by 1.2. Easy peasy, why not include that in the rebuilding state?&lt;/p&gt;
&lt;p&gt;Well, what if the tax rate changes? Does this mean that suddenly, all products you sold with a 20% VAT were actually sold with a 10% VAT? Of course not!&lt;/p&gt;
&lt;p&gt;The reconstruction phase will have to include some more logic to deal with the tax rate change, such as figuring out the right tax rate based on the date of the event.&lt;/p&gt;
&lt;p&gt;The second example concerns an expiration date. Imagine that buyers can add items to their cart for 20 days after the basket was created. Since we know the date of the &lt;code&gt;BasketWasCreated&lt;/code&gt; event, we can easily compute the expiration date in the reconstruction phase. This is as simple as taking one date and adding a duration.&lt;/p&gt;
&lt;p&gt;But here again, what if the business experts come to you and ask you to change the duration, reducing it from 20 days to 10 days? Should you say no to all users who believed they could act after 20 days, only after 10 days, now that the rule has changed? Probably not.&lt;/p&gt;
&lt;p&gt;These rules come from our business experts. They told us about the tax rate and duration. The computation should go into the command part of the system, and the result should be stored in the event.&lt;/p&gt;
&lt;p&gt;With the two examples, &lt;strong&gt;we also saw that other interesting questions to ask yourself are: Is it possible that this business rule might change at some point? And if so, what is the impact?&lt;/strong&gt; But as I said in the introduction, the trouble starts when we believe that the logic is stable, which makes this guideline less interesting than just looking at who told you about that logic.&lt;/p&gt;
&lt;p&gt;Let’s move to another idea.&lt;/p&gt;
&lt;h2 id=&quot;it-should-be-about-state-representation%2C-nothing-more.&quot; tabindex=&quot;-1&quot;&gt;It should be about state representation, nothing more.&lt;/h2&gt;
&lt;p&gt;Now that we’ve seen a heuristic for detecting what should go in the handling command part of our event-sourced system, let’s look at what should go in the reconstruction phase.&lt;/p&gt;
&lt;p&gt;The reconstruction phase consists of applying all past events to rebuild a state that will help to make future decisions. This is nothing more than a particular live projection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The logic in the rebuild state should only be about data structures. If you can find multiple ways to represent the information you are probably on track.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;For instance, let’s imagine that we have a (probably stupid) business rule that prevents people from buying more than 10 products at once. In the rebuild phase we could take the number of products added in the basket when we apply each &lt;code&gt;ProductAddedToBasket&lt;/code&gt; event and sum that, having only an integer in the state. Alternatively, we could rebuild the entire basket with a collection of all products and use &lt;code&gt;products.length&lt;/code&gt; when we handle the command to add a new product to the basket.&lt;/p&gt;
&lt;p&gt;We found two ways of representing the state. And, by the way, business experts don’t care which one we use. This is clearly reconstruction logic.&lt;/p&gt;
&lt;h2 id=&quot;are-you-doing-computation-on-one-event-only%3F&quot; tabindex=&quot;-1&quot;&gt;Are you doing computation on one event only?&lt;/h2&gt;
&lt;p&gt;Another idea that can help prevent the mistake of putting business logic in the wrong place is to look for areas where computation is being performed in the rebuild phase based on data contained in only one event.&lt;/p&gt;
&lt;p&gt;Let’s retake all previous examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;To compute the prices, including taxes, we would only need data from one &lt;code&gt;ProductAddedToBasket&lt;/code&gt; event. We are not making calculations across multiple events, just from one.&lt;/li&gt;
&lt;li&gt;To get the expiration date, this is the same thing. We are only doing calculations using data coming from the &lt;code&gt;BasketStarted&lt;/code&gt; event. That logic should go in the command handling part of the system.&lt;/li&gt;
&lt;li&gt;To have the entire quantity of products added to the basket, we need to look at multiple &lt;code&gt;ProductAddedToBasket&lt;/code&gt; events. This looks good, and the logic belongs to the rebuild phase.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Be careful; this heuristic comes with some limitations. Depending on how you represent the information in the events, you could need to do some computation based only on the data from one event.&lt;br&gt;
For instance, you could store the tax-free price and the tax rate in the &lt;code&gt;ProductAddedToBasket&lt;/code&gt; event. In that case, you would need to recompute the price, including VAT, based on these two pieces of data.&lt;br&gt;
The same is true for the other example. Instead of storing the expiration date, you could store the start date and the duration before expiration.&lt;/p&gt;
&lt;p&gt;As we can see, this is not a definitive rule; it is just something to look for.&lt;br&gt;
That heuristic could be refined to &lt;strong&gt;watch out for logic using data not coming from events.&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Or, in yet another form: Be very suspicious about every constant value in the rebuilding logic.&lt;/strong&gt;&lt;br&gt;
Look for hardcoded values, even hidden behing constants and ask you if it that could go in the command handling part.&lt;/p&gt;
&lt;p&gt;We probably can find other clues that we are performing business logic in the rebuild state. These are mine - I might even have other unconscious ones; who knows? - and I hope that they will be useful to you.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;When rebuilding the aggregate, if you will. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Reading a story across multiple causal links without losing your mind</title>
      <link href="https://blog.charlesdesneuf.com/articles/reading-a-story-across-multiple-causal-links-without-losing-your-mind/"/>
      <updated>2024-08-30T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/reading-a-story-across-multiple-causal-links-without-losing-your-mind/</id>
      <summary>Reading systemic causal loop diagrams is not always easy, especially if you try to tell a story that needs more than one relation, as they often do.</summary>
      <content type="html">&lt;p&gt;I often find it difficult to read multiple causal links in a row in causal loop diagrams, a tool used in System Thinking to describe the relation between elements (or stocks).&lt;/p&gt;
&lt;p&gt;Unfortunately for me, best stories often include more than one causal link between two stocks, or we wouldn’t need a tool like causal loop diagrams to understand systems. I want to share the trick I use to tell stories that make sense, involving multiple jumps without losing my mind and inverting some relationships.&lt;/p&gt;
&lt;h2 id=&quot;recap-on-reading-causal-links&quot; tabindex=&quot;-1&quot;&gt;Recap on reading causal links&lt;/h2&gt;
&lt;p&gt;Let’s start with a quick recap on reading one causal link in diagrams.&lt;/p&gt;
&lt;p&gt;A line between two stocks represents an impact.&lt;br&gt;
One line from one stock to another means that both stocks move in the same direction.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphviz&quot;&gt;---
className: diagram-25
---
strict digraph { 
  rankdir=&amp;quot;LR&amp;quot;
  A -&amp;gt; B
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, more of A leads to more of B, or less of A leads to less of B.&lt;/p&gt;
&lt;p&gt;But sometimes stocks move in opposite directions. Multiple notation exists, some with a dot (&lt;code&gt;●&lt;/code&gt;), others with a minus (&lt;code&gt;-&lt;/code&gt;). For the rest of the article, I’ll keep with the dot notation as I find it easier to read.&lt;/p&gt;
&lt;p&gt;So, a line with a &lt;code&gt;●&lt;/code&gt; means the stocks move in opposite directions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphviz&quot;&gt;---
className: diagram-25
---
strict digraph { 
  rankdir=&amp;quot;LR&amp;quot;
  A -&amp;gt; B [label=&amp;quot;●&amp;quot;]
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, more of A leads to less of B, or less of A leads to more of B.&lt;/p&gt;
&lt;h2 id=&quot;chaining-multiple-causal-links&quot; tabindex=&quot;-1&quot;&gt;Chaining multiple causal links&lt;/h2&gt;
&lt;p&gt;When reading a causal loop diagram with multiple links, the trick is to stop trying to be clever and read each link on its own, one after the other, and, instead, read them all in one go by switching from &amp;quot;more&amp;quot; to &amp;quot;less&amp;quot; every time you encounter a &lt;code&gt;●&lt;/code&gt; as you are telling the story.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphviz&quot;&gt;---
className: diagram-25
---
strict digraph { 
  A -&amp;gt; B
  B -&amp;gt; C [label=&amp;quot;●&amp;quot;]
  C -&amp;gt; A
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can read the diagram above as &amp;quot;more of A leads to more of B, more of B leads to &lt;code&gt;*switch*&lt;/code&gt; less of C, and less of C leads to less of A&amp;quot; or &amp;quot;less of A leads to less of B, less of B leads to &lt;code&gt;*switch*&lt;/code&gt; more of C, and more of C leads to more of A.&amp;quot;&lt;/p&gt;
&lt;p&gt;That trick even works when the loop goes across multiple inversions.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-graphviz&quot;&gt;---
className: diagram-25
---
strict digraph { 
  A -&amp;gt; B
  B -&amp;gt; C [label=&amp;quot;●&amp;quot;]
  C -&amp;gt; A [label=&amp;quot;●&amp;quot;]
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here, &amp;quot;more of A leads to more of B, more B leads to &lt;code&gt;*switch*&lt;/code&gt; less of C, and less of C leads to &lt;code&gt;*switch*&lt;/code&gt; more  of A&amp;quot; or &amp;quot;less of A leads to less of B, less of B leads to &lt;code&gt;*switch*&lt;/code&gt; more of C, and more of C leads to &lt;code&gt;*switch*&lt;/code&gt; less of A.&amp;quot;&lt;/p&gt;
&lt;p&gt;I hope you’ll find this helpful the next time you try to tell a story involving feedback loops!&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Collecting data via Google Form and Apple Shortcuts</title>
      <link href="https://blog.charlesdesneuf.com/articles/collecting-data-via-google-form-and-apple-shortcuts/"/>
      <updated>2024-09-06T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/collecting-data-via-google-form-and-apple-shortcuts/</id>
      <summary>Setup an Apple Shortcut to send data to a spreadsheet right from your phone</summary>
      <content type="html">&lt;p&gt;A while ago, I started tracking my weight using a &lt;a href=&quot;/tags/process-behavior-charts/&quot;&gt;Process Behavior Chart&lt;/a&gt; (PBC) but stopped, partly because the weighing machine was out of battery, I didn’t get the result I wanted, and because the app I built with &lt;a href=&quot;https://about.appsheet.com/home/&quot;&gt;AppSheet&lt;/a&gt; took too long to load, which was annoying.&lt;/p&gt;
&lt;p&gt;Now that I have repurchased some battery, I’m up again for the challenge of losing some weight, and I want to give another try using a PBC to see any change, but I don’t want to use AppSheet again and decided to use an Apple Shortcut instead to collect the data easily.&lt;/p&gt;
&lt;p&gt;So here we are: how to set up Google Form and an Apple Shortcut to collect measurements right from your phone to a spreadsheet.&lt;/p&gt;
&lt;h1 id=&quot;step-1.-create-a-google-form&quot; tabindex=&quot;-1&quot;&gt;Step 1. Create a Google Form&lt;/h1&gt;
&lt;p&gt;Head to &lt;a href=&quot;https://www.google.fr/intl/fr/forms/about/&quot;&gt;Google Form homepage&lt;/a&gt;, click &amp;quot;Access to Forms&amp;quot;, then create a new form.&lt;/p&gt;
&lt;p&gt;Next, add as many answers as you need.&lt;/p&gt;
&lt;p&gt;In my case, I needed one single answer, a decimal number greater than 0.&lt;br&gt;
&lt;img src=&quot;google_form_1.png&quot; alt=&quot;View of the Google form setup with one answer&quot;&gt;&lt;/p&gt;
&lt;p&gt;Depending on your phone language settings, the Apple shortcut might send decimal numbers formatted using a comma but Google Form won’t accept that as an answer. Instead of using a number input, use a Regular Expression with &lt;code&gt;^-?[&#92;d*&#92;,{1-9}]*$&lt;/code&gt; as validation rule.&lt;br&gt;
&lt;img src=&quot;google_form_2.png&quot; alt=&quot;View of the Google form setup with one answer using the regex&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; Alternatively, you can change the spreadsheet settings to simplify the process. In Google Sheet, go to &amp;quot;Files &amp;gt; Parameters&amp;quot; and select &amp;quot;United-States&amp;quot; for Regional Parameters.&lt;/p&gt;
&lt;p&gt;Part 1, done!&lt;/p&gt;
&lt;h1 id=&quot;step-2.-prepare-the-form-submission-url&quot; tabindex=&quot;-1&quot;&gt;Step 2. Prepare the form submission URL&lt;/h1&gt;
&lt;p&gt;In the form configuration panel, click on the three dots on the top right of the page and click &amp;quot;Get a Pre-Filled link&amp;quot;. A page opens. A Pre-Filled form is a way to share a form pre-filled with some information to someone else, to help them input the form faster or for reviewing data before submission.&lt;/p&gt;
&lt;p&gt;You will see the form displayed. Enter some data easily, distinguishable. I entered &lt;code&gt;100&lt;/code&gt;. This data will not be added to the results when you submit.&lt;/p&gt;
&lt;p&gt;Click &amp;quot;Get the link&amp;quot; and copy it. This is the URL we will use to submit new data from shortcut to the form.&lt;/p&gt;
&lt;p&gt;Here is the link I got:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;https://docs.google.com/forms/d/e/[REDACTED]/viewform?usp=pp_url&amp;amp;entry.1061001024=100&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Look closely, we can see the &lt;code&gt;100&lt;/code&gt;. This is the value we will replace with the each measurement.&lt;/p&gt;
&lt;p&gt;Edit the URL to be able to send the new measurements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;viewform&lt;/code&gt; with &lt;code&gt;formResponse&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Add &lt;code&gt;&amp;amp;submit=Submit&lt;/code&gt; at the end&lt;/li&gt;
&lt;li&gt;Replace the dummy value with ``&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You should get something like &lt;code&gt;https://docs.google.com/forms/d/e/[REDACTED]/formResponse?usp=pp_url&amp;amp;entry.1061001024={value}&amp;amp;submit=Submit&lt;/code&gt;&lt;/p&gt;
&lt;h1 id=&quot;step-3.-setup-the-apple-shortcut&quot; tabindex=&quot;-1&quot;&gt;Step 3. Setup the Apple Shortcut&lt;/h1&gt;
&lt;p&gt;On your phone, or directly from laptop, if you have a mac, open the Shortcut app and create a new shortcut.&lt;/p&gt;
&lt;p&gt;Then:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add the &amp;quot;Ask for input&amp;quot; step and set it up to receive numbers. In the options tab, allow for decimal numbers.&lt;/li&gt;
&lt;li&gt;Add the &amp;quot;Text&amp;quot; step and copy the form submission URL template created in Step 2.&lt;/li&gt;
&lt;li&gt;Add the &amp;quot;Replace text&amp;quot; step and replace &lt;code&gt;{value}&lt;/code&gt; with the data from the input on the text.&lt;/li&gt;
&lt;li&gt;Add the &amp;quot;Get content at URL&amp;quot; step and use the updated text as the URL.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your shortcut should look roughly like this&lt;br&gt;
&lt;img src=&quot;shortcut.png&quot; alt=&quot;View of the Apple Shortcut&quot;&gt;&lt;/p&gt;
&lt;p&gt;And you should be all set. For convenience, add the shortcut to your home screen for direct access.&lt;/p&gt;
&lt;p&gt;To input a measurement, click the shortcut icon, enter a value, press &amp;quot;OK&amp;quot;, and you’re done.&lt;/p&gt;
&lt;p&gt;A simple trick, just what it takes of development to make life easier. This is a nice alternative solution.&lt;/p&gt;
&lt;p&gt;In the following articles, I’ll &lt;a href=&quot;/articles/a-quick-introduction-to-process-behavior-charts/&quot;&gt;explain how to determine if data variation is meaningful (whether you actually lost or gained weight) using a Process Behavior Chart&lt;/a&gt;, and &lt;a href=&quot;/articles/building-a-pbc-in-google-sheet/&quot;&gt;how to build a PBC&lt;/a&gt;.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Create a new file at the right place with prefilled content thanks to Jetbrains IDEs</title>
      <link href="https://blog.charlesdesneuf.com/articles/create-a-new-file-at-the-right-place-with-prefilled-content-thanks-to-jetbrains-ides/"/>
      <updated>2024-09-13T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/create-a-new-file-at-the-right-place-with-prefilled-content-thanks-to-jetbrains-ides/</id>
      <summary>Streamline your JetBrains IDE workflow by automatically creating files with prefilled content in the correct directory structure for consistent development.</summary>
      <content type="html">&lt;p&gt;It’s been a while since I felt that pain...&lt;br&gt;
I had to manually create a file for every blog post in the correct directory, repeating information based on the title in snake case, using the current date, with the same structure that I copied from one old post to the new one and manually edited.&lt;/p&gt;
&lt;p&gt;That’s just too long, or at least super interesting and error-prone.&lt;/p&gt;
&lt;p&gt;I wanted to have a template for my blog post article. My articles are written in an &lt;code&gt;index.md&lt;/code&gt; stored in directories including the current dates and using the article’s title in snake case.&lt;/p&gt;
&lt;p&gt;For instance, the article you’re currently reading is stored in &lt;code&gt;/articles/2024-09-13-create-a-new-file-at-the-right-place-with-prefilled-content-thanks-to-jetbrains-ides/index.md&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Creating the directory and file, setting up some information about the permalink, and linking to the social image—all this after converting the article title to snake case—could probably be easier.&lt;/p&gt;
&lt;p&gt;For &lt;a href=&quot;https://burritalks.io/&quot;&gt;my Burritalks project&lt;/a&gt;, I created a small CLI in node that helps me set up pages faster. I could do the same here, but a question popped into my mind:&lt;/p&gt;
&lt;p&gt;Could Jetbrains IDE automate part of this?&lt;/p&gt;
&lt;p&gt;Of course!&lt;/p&gt;
&lt;p&gt;Here is what you can get:&lt;/p&gt;
&lt;iframe width=&quot;560&quot; height=&quot;560&quot; src=&quot;https://www.youtube.com/embed/SQN7go-N7Hg?si=6YGUzU_xgpxAGSKP&quot; title=&quot;Demonstration of the template in action&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;I tested that on Webstorm, but I’m pretty sure it would work on all their other IDEs. You can try that on Intellij, Rider, PhpStorm, PyCharm, Goland, ...&lt;/p&gt;
&lt;p&gt;Here is how it works:&lt;/p&gt;
&lt;p&gt;In your IDE settings, go to &lt;code&gt;Editor &amp;gt; File and Code Templates&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Create a new template by clicking on &lt;code&gt;+&lt;/code&gt; and giving it a name. In my case, I need to create a new blog post article so I went with &lt;code&gt;Article&lt;/code&gt;.&lt;/p&gt;
&lt;h1 id=&quot;automating-the-content-of-the-article&quot; tabindex=&quot;-1&quot;&gt;Automating the content of the article&lt;/h1&gt;
&lt;p&gt;The File template system makes use of &lt;a href=&quot;https://velocity.apache.org/engine/2.3/user-guide.html&quot;&gt;Apache Velocity template&lt;/a&gt;, and comes with a set of &lt;a href=&quot;https://www.jetbrains.com/help/idea/file-template-variables.html#variable-methods&quot;&gt;prepared variables&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In my case, I needed &lt;code&gt;${YEAR}&lt;/code&gt;, &lt;code&gt;${MONTH}&lt;/code&gt;, and &lt;code&gt;${DAY}&lt;/code&gt;  for the first part of my article path.&lt;/p&gt;
&lt;p&gt;You can also add custom variables. If the IDE finds a variable it doesn’t know when the template is applied, it will ask for a value. For example, I defined a &lt;code&gt;$TITLE&lt;/code&gt; variable to get the title of the article.&lt;/p&gt;
&lt;p&gt;Even, better, you can run some functions on your variable. A good thing when you want to turn the title to snake-case !&lt;/p&gt;
&lt;p&gt;I defined a new variable, &lt;code&gt;$TODASH&lt;/code&gt;, based on the &lt;code&gt;$TITLE&lt;/code&gt; one, which makes it lowercase and replaces spaces with a hyphen: &lt;code&gt;#set( $TODASH = $TITLE.toLowerCase().replaceAll(&amp;quot; &amp;quot;, &amp;quot;-&amp;quot;))&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;My complete template looks like so:&lt;/p&gt;
&lt;pre class=&quot;language-md&quot;&gt;&lt;code class=&quot;language-md&quot;&gt;&lt;span class=&quot;token title important&quot;&gt;#set( $TODASH = $TITLE.toLowerCase().replaceAll(&quot; &quot;, &quot;-&quot;))&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;&lt;br&gt;title: ${TITLE} &lt;br&gt;description: &quot;&quot;&lt;br&gt;permalink: /articles/${TODASH}/&lt;br&gt;socialImage: /articles/${YEAR}-${MONTH}-${DAY}-${TODASH}/social.png&lt;br&gt;language: en&lt;br&gt;tags:&lt;br&gt;  &lt;span class=&quot;token title important&quot;&gt;-&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is already super cool and saves some time, but I still needed to create the directory with the date and a snake-case version of the title.&lt;/p&gt;
&lt;p&gt;It turns out that can be automated as well!&lt;/p&gt;
&lt;h1 id=&quot;automating-the-directory-creation&quot; tabindex=&quot;-1&quot;&gt;Automating the directory creation&lt;/h1&gt;
&lt;p&gt;The File Name input runs the template systems, which means we can use Apache Velocity variables and functions there too.&lt;/p&gt;
&lt;p&gt;Here is the file name I defined :&lt;/p&gt;
&lt;p&gt;&lt;code&gt;src/articles/${YEAR}-${MONTH}-${DAY}-$TITLE.toLowerCase().replaceAll(&amp;quot; &amp;quot;, &amp;quot;-&amp;quot;)/index&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;See how it uses the same ideas as in the body template&lt;/p&gt;
&lt;p&gt;The extension, &lt;code&gt;.md&lt;/code&gt;, must be set in the Extension input to avoid doubling it.&lt;/p&gt;
&lt;p&gt;Now, I just need to add a new &amp;quot;Article&amp;quot;, type the name, and I get the &lt;code&gt;index.md&lt;/code&gt; file right at the right place and part of the front matter data already setup.&lt;/p&gt;
&lt;p&gt;And it’s time to start typing!&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>A quick introduction to Process Behavior Charts</title>
      <link href="https://blog.charlesdesneuf.com/articles/a-quick-introduction-to-process-behavior-charts/"/>
      <updated>2024-09-26T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/a-quick-introduction-to-process-behavior-charts/</id>
      <summary>Discover what Process Behavior Charts (PBCs) are, their origins, and how they help you stop overreacting to data variations for better decision-making.</summary>
      <content type="html">&lt;p&gt;On this blog, I’ve already spoken about &lt;a href=&quot;/tags/process-behavior-charts/&quot;&gt;Process Behavior Charts (PBC)&lt;/a&gt;, mostly to share that &lt;a href=&quot;/articles/pbc-a-christmas-tale/&quot;&gt;I wrote a little Christmas story to explain what they are and how they are used&lt;/a&gt;, but I never explained how to build one. That was the original plan for this article, but as I explained what PBCs are and why you might want to use them, the article turned into a very long piece, and I think it’s better to split it into two. &lt;a href=&quot;/articles/building-a-pbc-in-google-sheet/&quot;&gt;The article about building the PBC is here&lt;/a&gt;.&lt;/p&gt;
&lt;h1 id=&quot;what-are-process-behavior-charts-and-why-are-they-useful%3F&quot; tabindex=&quot;-1&quot;&gt;What are Process Behavior Charts and Why Are They Useful?&lt;/h1&gt;
&lt;p&gt;Have you ever looked at some data and seen it going up or down?&lt;br&gt;
What do you do when it goes up? What do you do when it goes down?&lt;br&gt;
Do you react whenever a data point doesn’t look like what you hoped it would be?&lt;/p&gt;
&lt;p&gt;Here’s the good news: you should stop reacting to every data point.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;In every data set you look at, you’ll see some variation, but not all variations are equal. In every process, some variation is expected; this is routine variation. Reacting to routine variation is just a waste of time because you can’t do anything about it. If you are unhappy with what you see, you must change the system producing that data. On the other hand, from time to time, something unexpected occurs—something outside the usual way of working pops up. Here, you should react and figure out why something unfamiliar just happened. You want to learn about it.&lt;/strong&gt; Did it produce something better than usual? Why is that? Can we change our system to have more of that? Was it bad? How can we change the system to have less of that?&lt;/p&gt;
&lt;p&gt;This leads to a question: How can you sort routine variation from special-cause variation? How can you know when to react?&lt;/p&gt;
&lt;p&gt;Process Behavior Charts are tools designed to do just that. They help distinguish signals (special cause variation) from noise (common cause variation).&lt;/p&gt;
&lt;p&gt;These charts were pioneered by Walter A. Shewhart in the 1920s and later popularized by W. Edwards Deming and Donald J. Wheeler, who further refined their use in quality management.&lt;/p&gt;
&lt;h2 id=&quot;how-do-they-work%3F&quot; tabindex=&quot;-1&quot;&gt;How Do They Work?&lt;/h2&gt;
&lt;p&gt;The idea is to draw a graph (actually two, but I’ll only show you how to build the first one) displaying your data alongside two limits: the Upper Natural Process Limit (UNPL) and the Lower Natural Process Limit (LNPL). &lt;strong&gt;You don’t get to choose what these limits are; they are calculated from the data available. They are an indication of how your system is working, not how you would like it to be.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;With these charts come a few rules for detecting signals. Something interesting is happening if you see:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Rule 1:&lt;/strong&gt; Any single point outside of the natural process limits.&lt;br&gt;
&lt;strong&gt;Rule 2:&lt;/strong&gt; Eight consecutive points on the same side of the central line.&lt;br&gt;
&lt;strong&gt;Rule 3:&lt;/strong&gt; Three out of four consecutive data points that are closer to the same limit than they are to the central line.&lt;/p&gt;
&lt;p&gt;These are the rules shared by Mark Graban in his book &lt;a href=&quot;https://www.measuresofsuccessbook.com/&quot;&gt;Measures of Success&lt;/a&gt;. Note that other rules exist and that you might encounter them if you continue digging into this subject.&lt;/p&gt;
&lt;h2 id=&quot;how-to-compute-the-data&quot; tabindex=&quot;-1&quot;&gt;How to Compute the Data&lt;/h2&gt;
&lt;ol start=&quot;0&quot;&gt;
&lt;li&gt;Collect the data. You want to make sure that you &lt;a href=&quot;https://xmrit.com/articles/why-defining-your-metrics-important/&quot;&gt;have a defined process for data collection to ensure that it’s always done the same way&lt;/a&gt;. You should probably describe that process alongside your graph or at the place you are collecting the data.&lt;/li&gt;
&lt;li&gt;Make sure the data is time-ordered.&lt;/li&gt;
&lt;li&gt;Choose a baseline period of at least 6 points and less than 20 that you think properly characterizes your process. Avoid using data if you know something uncommon occurred at the moment of measurement.&lt;/li&gt;
&lt;li&gt;Plot the data as a running record.&lt;/li&gt;
&lt;li&gt;Plot the central line. This is the arithmetic mean of your baseline point values (BaselineAverage).&lt;/li&gt;
&lt;li&gt;Calculate the moving range. This is the absolute value between two successive points (BaselineMovingRangeAverage).&lt;/li&gt;
&lt;li&gt;Calculate the Natural Process Limits:
&lt;ul&gt;
&lt;li&gt;UNPL = BaselineAverage + (2.66 * BaselineMovingRangeAverage)&lt;/li&gt;
&lt;li&gt;LNPL = BaselineAverage - (2.66 * BaselineMovingRangeAverage)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Plot the NPLs on the Running Record.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And here is the kind of chart you should see:&lt;br&gt;
&lt;img src=&quot;./graph.png&quot; alt=&quot;A XChart (PBC) about my weight&quot;&gt;&lt;br&gt;
I’ve started working on automating signal detection, and you can see a Rule 1 signal with one point above the UNPL.&lt;/p&gt;
&lt;p&gt;In the next article, I’ll share how to build one in Google Sheets based on the &lt;a href=&quot;/articles/collecting-data-via-google-form-and-apple-shortcuts/&quot;&gt;data collected via Google Forms and Apple Shortcuts&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;want-to-know-more-about-pbcs%3F&quot; tabindex=&quot;-1&quot;&gt;Want to Know More About PBCs?&lt;/h2&gt;
&lt;p&gt;This is a super, super short introduction to PBCs. You can find some really good resources out there that will dig deeper than I will, with better explanations and details. I was primarily interested in showing my entire workflow from data collection on my phone, PBC calculation, and PBC displayed on the phone, and raising a little awareness about this tool.&lt;/p&gt;
&lt;p&gt;Donald Wheeler’s &lt;em&gt;Understanding Variation&lt;/em&gt; is still sitting on my bookshelf, but I’ve heard it’s an interesting read and the source of learning for the authors of the next resources I’m about to share.&lt;br&gt;
I learned about PBCs from &lt;a href=&quot;https://www.measuresofsuccessbook.com/&quot;&gt;Mark Graban’s book &lt;em&gt;Measures of Success&lt;/em&gt;&lt;/a&gt;. This is an easy read that clearly demonstrates why most of the time we use numbers quite poorly, leading to overreaction, pressure, and no real improvement.&lt;/p&gt;
&lt;p&gt;If you’re working in an Agile setting or using Kanban, Dan Vacanti’s &lt;a href=&quot;https://leanpub.com/actionableagilemetricsii&quot;&gt;&lt;em&gt;Actionable Agile Metrics Volume II&lt;/em&gt;&lt;/a&gt; shows how to use PBCs for flow metrics.&lt;/p&gt;
&lt;p&gt;If you are looking for a super good free resource, the team at CommonCog created &lt;a href=&quot;https://xmrit.com/&quot;&gt;a tool to create PBCs&lt;/a&gt; and offers a free one-week course over email that is super interesting along with articles.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Building a PBC in Google Sheet</title>
      <link href="https://blog.charlesdesneuf.com/articles/building-a-pbc-in-google-sheet/"/>
      <updated>2024-10-04T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/building-a-pbc-in-google-sheet/</id>
      <summary>Learn how to build a Process Behavior Chart (PBC) in Google Sheets using data collected from Google Forms and Apple Shortcuts. This step-by-step guide covers importing data, calculating key metrics, and visualizing data variation to enhance your data analysis process.</summary>
      <content type="html">&lt;p&gt;In the previous article, I shared &lt;a href=&quot;/articles/a-quick-introduction-to-process-behavior-charts/&quot;&gt;what Process Behavior Charts (PBC) are&lt;/a&gt; and promised to explain how to build one in Google Sheets, using the &lt;a href=&quot;/articles/collecting-data-via-google-form-and-apple-shortcuts/&quot;&gt;data collected from an Apple Shortcut&lt;/a&gt;. This article will cover that. Let’s get started.&lt;/p&gt;
&lt;p&gt;The article on collecting the data left us with a Google Sheet containing one sheet of data with all the measurements collected from the shortcut. You can access that spreadsheet from Google Forms by clicking on the &amp;quot;Responses&amp;quot; tab.&lt;/p&gt;
&lt;p&gt;You can rename the first sheet, containing the measurements, to something that suits you better than &amp;quot;Responses.&amp;quot; I decided to rename it &amp;quot;Measurements.&amp;quot;&lt;/p&gt;
&lt;p&gt;You won’t need to touch that sheet anymore; we will work on another sheet for the rest of this article.&lt;/p&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Do not work on the sheet made for collecting data&lt;/p&gt;
&lt;p&gt;When working with spreadsheets, having a sheet that contains raw data helps with maintainability since you won’t be tempted to move columns around and break formulas elsewhere. If you’re into Software Architecture, you can view that as your write side in the CQRS pattern, and we will use other sheets for the read sides.&lt;/p&gt;
&lt;/div&gt;
&lt;h1 id=&quot;fetch-data-from-the-form-submissions-sheet-to-build-the-pbc&quot; tabindex=&quot;-1&quot;&gt;Fetch data from the form submissions sheet to build the PBC&lt;/h1&gt;
&lt;p&gt;Create another sheet where we will compute the &lt;a href=&quot;/tags/process-behavior-charts/&quot;&gt;PBC&lt;/a&gt; data for the measurements. I named mine &amp;quot;PBC.&amp;quot; You can’t make it more straightforward than that.&lt;/p&gt;
&lt;p&gt;In that sheet, get the data from the other sheet with the &lt;code&gt;={Measurements!A2:B}&lt;/code&gt; formula in the &lt;code&gt;A2&lt;/code&gt; cell.&lt;/p&gt;
&lt;p&gt;In the first row, in cells &lt;code&gt;A1&lt;/code&gt; and &lt;code&gt;B1&lt;/code&gt;, add titles for the two columns. I went with &amp;quot;Date&amp;quot; and &amp;quot;Measurements.&amp;quot;&lt;/p&gt;
&lt;div class=&quot;admonition tip&quot;&gt;
&lt;p class=&quot;admonition-title&quot;&gt;Why aren’t we getting all the data from the reference data sheet ?&lt;/p&gt;
&lt;p&gt;Why not get the value directly from the &lt;code&gt;A1&lt;/code&gt; cell to get the column’s name from the reference data sheet?&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;A1&lt;/code&gt; cell, we could use &lt;code&gt;={Measurements!A:B}&lt;/code&gt; to get all the data directly from the Measurements sheet. That seems like a good idea, but it actually isn’t.&lt;/p&gt;
&lt;p&gt;Google Forms keeps track of how many submissions were received and adds new submissions to the current count + 1 row. This is probably to avoid overwriting past values.&lt;br&gt;
During the setup process of the Apple Shortcut, you will be tempted to test it and see if everything works as expected. Unfortunately, due to Google Forms&#39; ways of working, you won’t be able to insert real, non-test data starting from the first row.&lt;/p&gt;
&lt;p&gt;Using the trick above, you can select the row at which your PBC starts reading real values.&lt;br&gt;
If the real values submitted from the form are inserted in row 17, you can edit the formula in the &lt;code&gt;A2&lt;/code&gt; cell to reflect that, using &lt;code&gt;={Measurements!A17:B}&lt;/code&gt; to get only the data you really care about in that sheet.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;You should now see values from the form in that spreadsheet. Each new value submitted via the form will end up in that sheet.&lt;/p&gt;
&lt;h1 id=&quot;compute-the-moving-range&quot; tabindex=&quot;-1&quot;&gt;Compute the moving range&lt;/h1&gt;
&lt;p&gt;In the first row of the C column, write &amp;quot;Moving Range.&amp;quot;&lt;/p&gt;
&lt;p&gt;The moving range is the absolute difference between one measurement and the one before. We can’t have a moving range value for the first measurement, as no previous measurement was made. In the &lt;code&gt;C3&lt;/code&gt; cell, add this formula: &lt;code&gt;=IF(ISBLANK(B3), &amp;quot;&amp;quot;, ABS(B3-B2))&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ABS(B3-B2)&lt;/code&gt; computes the difference between the second value and the first one and makes it a positive number.&lt;/p&gt;
&lt;p&gt;The other part of the formula prevents incorrect values from being displayed in rows without any measurement yet by leaving them blank. We will use this trick for all the other columns.&lt;/p&gt;
&lt;p&gt;Use the blue dot and drag down to set the formula for the following rows&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. I went down to row 100.&lt;/p&gt;
&lt;h2 id=&quot;compute-the-average&quot; tabindex=&quot;-1&quot;&gt;Compute the average&lt;/h2&gt;
&lt;p&gt;In the first row of the D column, write &amp;quot;Average.&amp;quot;&lt;/p&gt;
&lt;p&gt;I decided to go with 20 points for my baseline. To do the same, add &lt;code&gt;=IF(ISBLANK(B2), &amp;quot;&amp;quot;, AVERAGE(B$2:B$22))&lt;/code&gt; to the &lt;code&gt;D2&lt;/code&gt; cell.&lt;/p&gt;
&lt;p&gt;Again, use the blue dot and drag down to set the formula for the following rows.&lt;/p&gt;
&lt;h2 id=&quot;compute-the-average-moving-range&quot; tabindex=&quot;-1&quot;&gt;Compute the average moving range&lt;/h2&gt;
&lt;p&gt;In the first row of the E column, write &amp;quot;Average moving range.&amp;quot;&lt;/p&gt;
&lt;p&gt;For a baseline of 20 points, we need to compute the average of the first 20 moving ranges.&lt;/p&gt;
&lt;p&gt;To do this, add &lt;code&gt;=IF(ISBLANK(B2), &amp;quot;&amp;quot;, AVERAGE(C$2:C$22))&lt;/code&gt; in the &lt;code&gt;E2&lt;/code&gt; cell and use the blue dot once more to apply the formula for more rows.&lt;/p&gt;
&lt;h2 id=&quot;compute-the-lower-limit&quot; tabindex=&quot;-1&quot;&gt;Compute the Lower Limit&lt;/h2&gt;
&lt;p&gt;In &lt;code&gt;F1&lt;/code&gt;, write &amp;quot;Lower limit&amp;quot;, add the &lt;code&gt;=IF(ISBLANK(B2), &amp;quot;&amp;quot;, D2 - (3 * E2 / 1.128))&lt;/code&gt; in &lt;code&gt;F2&lt;/code&gt;, and use the blue dot.&lt;/p&gt;
&lt;h2 id=&quot;compute-the-upper-limit&quot; tabindex=&quot;-1&quot;&gt;Compute the Upper Limit&lt;/h2&gt;
&lt;p&gt;In &lt;code&gt;G1&lt;/code&gt;, write &amp;quot;Upper limit&amp;quot;, add the &lt;code&gt;=IF(ISBLANK(B2), &amp;quot;&amp;quot;, D2 + (3 * E2 / 1.128))&lt;/code&gt; in &lt;code&gt;G2&lt;/code&gt;, and use the blue dot one final time.&lt;/p&gt;
&lt;p&gt;Now, you have all the data you need to draw your XChart, the first of the two types of charts usually included in PBCs, and the only one we will build together.&lt;/p&gt;
&lt;p&gt;The other chart, the mRChart, helps detect signals of unusual variation between two consecutive measurements.&lt;/p&gt;
&lt;p&gt;You’ll often see the pair of the two charts referred to as XmR Charts.&lt;/p&gt;
&lt;h2 id=&quot;draw-the-graph&quot; tabindex=&quot;-1&quot;&gt;Draw the graph&lt;/h2&gt;
&lt;p&gt;Select columns A (Date), B (Measurement), D (Average), F (Lower Limit), and G (Upper Limit), and insert a new graph.&lt;/p&gt;
&lt;p&gt;You’ve drawn your first XChart with Google Sheets. Congrats!&lt;/p&gt;
&lt;p&gt;It’s probably unreadable, though. Please take a few minutes to adjust the settings and improve it.&lt;/p&gt;
&lt;p&gt;First, zoom in to view your data. In the vertical axis settings, add a minimum value.&lt;br&gt;
Also, change the graph title to give it a better name.&lt;/p&gt;
&lt;p&gt;Now that we see more than just a group of compacted lines, we can customize the series.&lt;/p&gt;
&lt;p&gt;For the Measurement series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make it black&lt;/li&gt;
&lt;li&gt;Add the data labels&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the Average series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make it blue&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For both the Upper and Lower Limits series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make them red&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;./social.png&quot; alt=&quot;My weight PBC nicely displayed&quot;&gt;&lt;/p&gt;
&lt;p&gt;And now you have a nicely displayed PBC, or more precisely, the XChart part of it.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;If someone knows a better way to set a formula for all following cells in a column, I’d be more than happy to hear it. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Run Laravel Artisan Tests directly in PhpStorm test view</title>
      <link href="https://blog.charlesdesneuf.com/articles/run-laravel-artisan-test-directly-from-phpstorm/"/>
      <updated>2024-10-11T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/run-laravel-artisan-test-directly-from-phpstorm/</id>
      <summary>Learn how to run Laravel tests directly in PhpStorm with a custom workaround for artisan test and PHPUnit.</summary>
      <content type="html">&lt;p&gt;One of the teams I work with uses Laravel and enjoys running tests via PhpStorm. Unfortunately, it’s not possible to run Laravel tests using &lt;code&gt;php artisan test&lt;/code&gt; directly within the IDE’s test interface. This forces us to run tests slower than with the CLI because they don’t run in parallel.&lt;/p&gt;
&lt;p&gt;You might think it’s possible to run Laravel tests using Paratest directly in PhpStorm, but this doesn’t work either due to how the database cleanup system is implemented—at least, that was the case the last time I tried.&lt;/p&gt;
&lt;p&gt;Since we prefer PhpStorm’s interface for running our tests, I wondered if it was possible to run Laravel tests directly inside PhpStorm’s test interface, and it turns out that it is—with a bit of a hack.&lt;/p&gt;
&lt;h2 id=&quot;get-the-wrapper&quot; tabindex=&quot;-1&quot;&gt;Get the Wrapper&lt;/h2&gt;
&lt;p&gt;Copy and paste the following code into a file named &lt;code&gt;phpunit&lt;/code&gt; somewhere in your project. In my case, I put it in &lt;code&gt;tooling/PhpstormLaravelTest/phpunit&lt;/code&gt;. The file needs to be named &lt;code&gt;phpunit&lt;/code&gt; because Paratest, used when you want to run multiple test classes at once (from the same directory, for instance), checks that specific filename.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;#!/usr/bin/env php  &lt;br&gt;&lt;span class=&quot;token php language-php&quot;&gt;&lt;span class=&quot;token delimiter important&quot;&gt;&amp;lt;?php&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;declare&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;strict_types&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// This will need to be named `/phpunit` if you want to be able to use that tool alongside paratest for phpstorm,  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// for instance when you are running all tests in a directory.  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// The best thing I came up with so far is to create a directory `PhpStormForLaravelTest` and create that file inside.  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;👋 Laravel Wrapper for Phpstorm PHPUnit 10.5.15-Fake. For the real PhpUnit and Paratest versions see below.&#92;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Artisan test doesn’t have a --filter argument, we need to use phpunit when running only part of the tests.  &lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;in_array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;--filter&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$argv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$command&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;php vendor/phpunit/phpunit/phpunit&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;// Add single quote for every argument to avoid escaping issues that prevent  &lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$args&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;array_slice&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$argv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$args&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;array_map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;&#39;${a}&#39;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$command&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39; &#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;implode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39; &#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;br&gt;    &lt;span class=&quot;token variable&quot;&gt;$command&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;php artisan test --parallel --teamcity&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string double-quoted-string&quot;&gt;&quot;🔍 Running &lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&#92;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;br&gt;  &lt;br&gt;&lt;span class=&quot;token function&quot;&gt;passthru&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, this is clearly a hack. There is no guarantee it will work in every situation. It’s a wrapper on top of a wrapper that uses a library wrapping the testing framework. You have to account for Paratest specifics (naming the file &lt;code&gt;/phpunit&lt;/code&gt;) and &lt;code&gt;artisan test&lt;/code&gt; specifics (switching to PHPUnit when the &lt;code&gt;--filter&lt;/code&gt; option is used because otherwise it doesn’t work).&lt;/p&gt;
&lt;p&gt;What this script does is simple: when there are no options, it uses &lt;code&gt;php artisan test --parallel&lt;/code&gt; to execute tests as fast as possible, while asking for TeamCity-compatible output that PhpStorm can parse and display in its test view. When you’re trying to run a single test or test class, PhpStorm will use the &lt;code&gt;--filter&lt;/code&gt; option, and in that case, we redirect to PHPUnit.&lt;/p&gt;
&lt;p&gt;A big hack, but one that serves us well.&lt;/p&gt;
&lt;h2 id=&quot;configure-phpstorm&quot; tabindex=&quot;-1&quot;&gt;Configure PhpStorm&lt;/h2&gt;
&lt;p&gt;Once installed, you need to configure it in PhpStorm.&lt;/p&gt;
&lt;p&gt;In PhpStorm settings, go to &lt;code&gt;PHP &amp;gt; Test Frameworks&lt;/code&gt;.&lt;br&gt;
&lt;img src=&quot;./test_frameworks_window.png&quot; alt=&quot;PhpStorm test frameworks setting window&quot;&gt;&lt;/p&gt;
&lt;p&gt;Add a new test runner by clicking the &lt;code&gt;+&lt;/code&gt; and selecting &lt;code&gt;PHPUnit by Remote Interpreter&lt;/code&gt;.&lt;br&gt;
&lt;img src=&quot;./select_remote_interpreter.png&quot; alt=&quot;Interpreter type window&quot;&gt;&lt;/p&gt;
&lt;p&gt;Select the interpreter that will run the tests. If you already have a test runner configured to use that interpreter, you’ll have to remove it, as PhpStorm only allows one test runner per interpreter.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./intepreter_selection_window.png&quot; alt=&quot;Interpreter selection window&quot;&gt;&lt;/p&gt;
&lt;p&gt;Configure the interpreter to use a &lt;code&gt;.phar&lt;/code&gt; file and point to the wrapper file with the correct mapping. It should look something like &lt;code&gt;/var/www/html/phpstorm-laravel-test-wrapper.php&lt;/code&gt;. Click the reload button to check if it works. You should see a fake PHPUnit version displayed.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./wrapper_configuration.png&quot; alt=&quot;Test framework settings&quot;&gt;&lt;/p&gt;
&lt;p&gt;In your test run settings, ensure you’re using the correct PHP interpreter—in our case, &lt;code&gt;laravel.test&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./run_configuration_settings.png&quot; alt=&quot;Run configuration setting window&quot;&gt;&lt;/p&gt;
&lt;p&gt;And there you go! Enjoy running your tests with speed and ease in PhpStorm 🏎️.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Using PhpStorm’s test interface with Laravel offers a much better developer experience, but it requires some clever configuration hacks. By setting up this wrapper, you can enjoy the best of both worlds: the speed of &lt;code&gt;php artisan test --parallel&lt;/code&gt; and the flexibility of PHPUnit for individual tests.&lt;/p&gt;
&lt;hr&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Streamlining Development with Custom Shell Functions: Reusing a command with a custom config for each project</title>
      <link href="https://blog.charlesdesneuf.com/articles/streamlining-development-with-custom-shell-functions/"/>
      <updated>2024-10-18T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/streamlining-development-with-custom-shell-functions/</id>
      <summary>Discover how to create custom shell functions for PHPUnit, Composer, and other tools to streamline your development workflow, especially when working across various tech stacks.</summary>
      <content type="html">&lt;p&gt;As a freelance technical agile coach, working across multiple projects means dealing with a variety of tech stacks. Every client has their own preferred tools, some with Docker, some without, and often there are custom setups or hacks. Over time, managing these different workflows can become mentally exhausting. You don’t want to waste time remembering specific commands for each environment or tool every time you start a project.&lt;/p&gt;
&lt;p&gt;To solve this, I’ve created custom shell functions to simplify and streamline my workflow. These helpers sit on top of the usual tools, allowing me to invoke commands without remembering intricate details or worrying about specific configurations for each project.&lt;/p&gt;
&lt;p&gt;Here’s how I did it.&lt;/p&gt;
&lt;h1 id=&quot;example%3A-custom-shell-function-command-for-phpunit&quot; tabindex=&quot;-1&quot;&gt;Example: Custom Shell Function Command for PHPUnit&lt;/h1&gt;
&lt;p&gt;Here’s an example of a custom bash function I’ve written for PHPUnit. This function is placed in my shell alias file.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function-name function&quot;&gt;phpunit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./my-conf&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;cmd&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; ^PHPUNIT ./my-conf &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;awk&lt;/span&gt; -F&lt;span class=&quot;token string&quot;&gt;&quot;PHPUNIT=&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;{print $2}&#39;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$cmd&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;eval&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$cmd&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$@&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-phpunit.xml&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;    vendor/bin/phpunit &lt;span class=&quot;token parameter variable&quot;&gt;-c&lt;/span&gt; my-phpunit.xml &lt;span class=&quot;token variable&quot;&gt;$@&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;br&gt;    vendor/bin/phpunit &lt;span class=&quot;token variable&quot;&gt;$@&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;breaking-it-down%3A&quot; tabindex=&quot;-1&quot;&gt;Breaking it down:&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Check for a custom configuration file&lt;/strong&gt;: The function first looks for a file called &lt;code&gt;my-conf&lt;/code&gt; in the current project directory. This file can override the default behavior by specifying custom commands.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extract custom PHPUnit command&lt;/strong&gt;: If &lt;code&gt;my-conf&lt;/code&gt; contains a line starting with &lt;code&gt;PHPUNIT=&lt;/code&gt;, the script grabs that line and uses &lt;code&gt;awk&lt;/code&gt; to extract the command that follows. This allows me to define a custom PHPUnit command per project if necessary. For instance, some projects require running PHPUnit inside a Docker container; I just don’t want to have to think about that.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Execute the custom command if found&lt;/strong&gt;: If a custom PHPUnit command is found, it is executed using &lt;code&gt;eval&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Fallbacks to default behavior&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;If no custom command is found, the function checks for a &lt;code&gt;my-phpunit.xml&lt;/code&gt; configuration file and runs PHPUnit with that. I don’t remember why I put that in, as it supposed to be the default PHPUnit behavior. No code is perfect, and at least you get the idea if you want to include some configuration in your command by default.&lt;/li&gt;
&lt;li&gt;If neither is available, it runs the default PHPUnit command from the vendor folder.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This setup allows me to run PHPUnit across multiple projects without having to remember specific configurations for each one.&lt;/p&gt;
&lt;p&gt;I’ve created such function for every tool that is reused accross project&lt;/p&gt;
&lt;h1 id=&quot;the-my-conf-file%3A-customizing-behavior-for-each-project&quot; tabindex=&quot;-1&quot;&gt;The &lt;code&gt;my-conf&lt;/code&gt; File: Customizing Behavior for Each Project&lt;/h1&gt;
&lt;p&gt;In each project, I can create a &lt;code&gt;my-conf&lt;/code&gt; file that overrides the default behavior for specific tools. This file is included in my global &lt;code&gt;.gitignore&lt;/code&gt;, so it doesn’t get committed to version control.&lt;/p&gt;
&lt;p&gt;Here’s an example of what a &lt;code&gt;my-conf&lt;/code&gt; file might look like for a Laravel project using Sail and Docker:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;PHPSTAN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;sail php ./vendor/bin/phpstan --memory-limit&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;-1  &lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;RECTOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;docker compose &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; my-docker-compose.yml run &lt;span class=&quot;token parameter variable&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-it&lt;/span&gt; rector ./bin/rector  &lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;COMPOSER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;sail &lt;span class=&quot;token function&quot;&gt;composer&lt;/span&gt;  &lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;PHPUNIT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;./bin/phpunit  &lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, I have custom commands for various tools like PHPStan, Rector, Composer, and PHPUnit. This file allows me to run these commands seamlessly without having to think about whether I need to use Sail or Docker in a specific project.&lt;/p&gt;
&lt;h1 id=&quot;why-use-custom-shell-functions%3F&quot; tabindex=&quot;-1&quot;&gt;Why Use Custom Shell Functions?&lt;/h1&gt;
&lt;p&gt;Using custom shell functions gives me several key advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Consistency&lt;/strong&gt;: I can run the same command across different projects without worrying about project-specific details.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Efficiency&lt;/strong&gt;: With these helpers in place, I don’t have to spend mental energy remembering how each tool is configured in each project.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;: I can easily adapt to new client setups by modifying the &lt;code&gt;my-conf&lt;/code&gt; file without touching the global setup.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Collaboration&lt;/strong&gt;: Teams can have their own configurations, while I keep my personalized workflow without impacting theirs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h1 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h1&gt;
&lt;p&gt;By creating custom shell functions for tools like PHPUnit, Composer, PHPStan, and others, I’ve streamlined my workflow significantly. These helpers give me the flexibility to work across multiple projects with different setups, while maintaining a consistent and efficient workflow.&lt;/p&gt;
&lt;p&gt;If you’re working across multiple projects and stacks like me, this method can save you valuable time and mental energy. And if your team isn’t quite ready to adopt these shortcuts, no worries — you can keep them in your local environment without disrupting the broader team workflow.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Displaying Process Behavior Charts on iPhone with Charty and Google Sheets</title>
      <link href="https://blog.charlesdesneuf.com/articles/displaying-process-behavior-charts-on-iphone-with-charty-and-google-sheets/"/>
      <updated>2024-11-06T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/displaying-process-behavior-charts-on-iphone-with-charty-and-google-sheets/</id>
      <summary>Master the integration of Google Sheets with Charty app to create real-time Process Behavior Chart visualizations on iPhone for data-driven decision making.</summary>
      <content type="html">&lt;p&gt;In this article, we’re building on our previous guide on &lt;a href=&quot;/articles/building-a-pbc-in-google-sheet/&quot;&gt;creating Process Behavior Charts (PBC) in Google Sheets&lt;/a&gt;, with (data submission automated through iOS Shortcuts and Google Forms)[/articles/collecting-data-via-google-form-and-apple-shortcuts/]. Now, we’ll close the loop by bringing this data back to your iPhone, visualizing it using the &lt;a href=&quot;https://chartyios.app/&quot;&gt;Charty app&lt;/a&gt;. With this setup, you’ll have your real-time PBC data right at your fingertips!&lt;/p&gt;
&lt;h2 id=&quot;preparing-data-for-export&quot; tabindex=&quot;-1&quot;&gt;Preparing Data for Export&lt;/h2&gt;
&lt;p&gt;First, let’s set up a new sheet in Google Sheets specifically for export. In your existing Google Sheets file, create a new sheet called &lt;strong&gt;&amp;quot;PBC Export&amp;quot;&lt;/strong&gt;. This sheet will serve as the source for data that will be published and accessible via Charty.&lt;/p&gt;
&lt;p&gt;Over time, your PBC data may accumulate a substantial number of points. To maintain a manageable display on your phone, you’ll want to control which data points are shown. Use the &lt;strong&gt;PBC Export&lt;/strong&gt; sheet to filter data from your main PBC sheet according to your needs. For instance, if you’d like to include only points after a particular date, add this formula to cell &lt;code&gt;A1&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;=FILTER(PBC!A1:G1000, PBC!A1:A1000 &amp;gt; DATE(2024,10,23))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This filter will retrieve only data from the &lt;strong&gt;PBC&lt;/strong&gt; sheet for dates later than October 23, 2024. You can adjust the date as necessary, or even reference another cell to make this date dynamic.&lt;/p&gt;
&lt;h2 id=&quot;publishing-the-data&quot; tabindex=&quot;-1&quot;&gt;Publishing the Data&lt;/h2&gt;
&lt;p&gt;Now that you’ve prepared your data, let’s make it publicly accessible so that an iOS Shortcut can fetch it. Follow these steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;In Google Spreadsheet, navigate to &lt;strong&gt;File &amp;gt; Share &amp;gt; Publish to the web&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;In the options, select the &lt;strong&gt;PBC Export&lt;/strong&gt; sheet instead of the entire document, and choose the &lt;strong&gt;CSV format&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Google Sheets will provide a link to this CSV file – copy this link as we’ll need it in the next step.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;creating-a-shortcut-for-charty&quot; tabindex=&quot;-1&quot;&gt;Creating a Shortcut for Charty&lt;/h2&gt;
&lt;p&gt;With your data link ready, we’ll now create a shortcut in the iOS Shortcuts app to feed this data into Charty.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open the &lt;strong&gt;Shortcuts&lt;/strong&gt; app and create a new shortcut.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add the following actions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Create a chart&lt;/strong&gt;: Name it something relevant, like “PBC Chart.”, or &amp;quot;Weight&amp;quot; in my case.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Get contents of URL&lt;/strong&gt;: Paste the URL from the previous step here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Add series&lt;/strong&gt;: Use the data from the “Contents of URL” action as a line series in your chart (this should be the “Chart Id” you created earlier). Assign:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Date&lt;/strong&gt; for X values.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Measurement&lt;/strong&gt;, &lt;strong&gt;Upper Limit&lt;/strong&gt;, &lt;strong&gt;Lower Limit&lt;/strong&gt;, and &lt;strong&gt;Average&lt;/strong&gt; for Y values.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;shortcut_start.jpg&quot; alt=&quot;Example setup for Charty Shortcut&quot;&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Add an action to &lt;strong&gt;Update all widgets&lt;/strong&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To complete the setup, add a Charty widget to your home screen and link it to the chart you just created.  You should now see your PBC data right on your iPhone!&lt;/p&gt;
&lt;h2 id=&quot;customizing-your-chart&quot; tabindex=&quot;-1&quot;&gt;Customizing Your Chart&lt;/h2&gt;
&lt;p&gt;But it’s probably not super nice to read.&lt;/p&gt;
&lt;p&gt;To enhance the visual clarity:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;For each Y value, add an action to &lt;strong&gt;Update Series Style&lt;/strong&gt;. Here’s a suggested styling:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Upper and Lower Limits&lt;/strong&gt;: Red (&lt;code&gt;#FF0000&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Average&lt;/strong&gt;: Blue (&lt;code&gt;#0000FF&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Measurement&lt;/strong&gt;: Black (&lt;code&gt;#000000&lt;/code&gt;), with markers enabled and marker size set to 2.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;An example of measurement settings:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;measurement_style.png&quot; alt=&quot;Measurement Settings&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here’s my final result with the PBC displayed on my iPhone. Looks great, right?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;widget.png&quot; alt=&quot;PBC on iPhone&quot;&gt;&lt;/p&gt;
&lt;h2 id=&quot;bonus-point%3A-setup-an-automation-to-refresh-the-chart-daily&quot; tabindex=&quot;-1&quot;&gt;Bonus point: Setup an automation to refresh the chart daily&lt;/h2&gt;
&lt;p&gt;On the shortcut app, create an automation that runs every day and executes the shortcut you’ve just created.&lt;br&gt;
That way you won’t have to manually trigger a refresh of the chart.&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot; tabindex=&quot;-1&quot;&gt;Wrapping Up&lt;/h2&gt;
&lt;p&gt;This is the final piece of that series about collecting data from an iPhone right into a Google sheet to build a PBC, and then displaying it on your phone.&lt;br&gt;
This flow gives you instant access to up-to-date charts, empowering better, data-informed decisions right from your home screen.&lt;/p&gt;
&lt;p&gt;This was fun to build.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>A clean commit history with Git WipSquash</title>
      <link href="https://blog.charlesdesneuf.com/articles/a-clean-commit-history-with-git-wipsquash/"/>
      <updated>2025-01-10T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/a-clean-commit-history-with-git-wipsquash/</id>
      <summary>Learn a lazy guy maintains clean and atomic commit histories, with the `wipsquash` script.</summary>
      <content type="html">&lt;p&gt;Maintaining clean and meaningful commit histories in Git is crucial for collaboration and code maintenance.&lt;br&gt;
Yet, the process of ensuring every commit is atomic can feel tedious, especially during active development.&lt;br&gt;
&lt;strong&gt;I like to do super small commits&lt;/strong&gt;, especially when I’m refactoring code or working on something I’m not super sure about, but I’m way too lazy to type a meaningful message each time.&lt;/p&gt;
&lt;p&gt;Maybe someday I’ll write a Jetbrains IDE extension that generates commit messages after an automated refactoring, but as that doesn’t exist yet, I sometimes go with a commit containing only &lt;code&gt;wip&lt;/code&gt; (for Work In Progress).&lt;br&gt;
It takes no time to create, you can even have an alias for it, but it left you with a commit history that you are not proud of, and clearly not meaningful at all.&lt;/p&gt;
&lt;p&gt;I wanted to find a solution and finally took some time to work on it and create a script that solves my pain point.&lt;br&gt;
&lt;strong&gt;I’m currently experimenting with it and with that workflow&lt;/strong&gt;, and I wanted to share it with others to see if some people find that idea useful.&lt;/p&gt;
&lt;h2 id=&quot;enters-git-wipsquash&quot; tabindex=&quot;-1&quot;&gt;Enters &lt;code&gt;git wipsquash&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Let’s talk about &lt;code&gt;git wipsquash&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The idea for &lt;code&gt;wipsquash&lt;/code&gt; is that you make an effort to say what you are doing only once, at the start.&lt;br&gt;
This goes well with atomic/&lt;a href=&quot;https://www.conventionalcommits.org/&quot;&gt;conventional commits&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Imagine you’re working on a feature or bug fix. You start with an intentional, well-named commit, such as:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; “Add authentication middleware”&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, as you continue tweaking, experimenting, or debugging, you add several more commits, hastily labeled &lt;code&gt;wip&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; “wip”&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; “wip”&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you’re done. This is a great, good job, but the commit history looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;. Add authentication middleware&lt;br&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;. wip&lt;br&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;. wip&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Will you be brave enough to push that to the shared repo with your colleagues?&lt;br&gt;
What do you feel about that commit history?&lt;br&gt;
I don’t know about you, but I know that I feel a bit ashamed.&lt;/p&gt;
&lt;p&gt;This is the time to call &lt;code&gt;wipsquash&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;wipsquash&lt;/code&gt; allows you to combine your &lt;code&gt;wip&lt;/code&gt; commits with the nearest meaningful one, creating clean, atomic commits without disrupting your workflow.&lt;/p&gt;
&lt;p&gt;Calling &lt;code&gt;git wipsquash&lt;/code&gt; merges the three commits together and keeps them at one, properly named &lt;code&gt;Add authentication middleware&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When &lt;code&gt;git wipsquash&lt;/code&gt; is called without any parameter, it will squash up to the first past commit with a meaningful name (defined here a not containing &lt;code&gt;wip&lt;/code&gt; or &lt;code&gt;WIP&lt;/code&gt; or any variation of that).&lt;br&gt;
When called with a commit hash, the script will run will squash up to that commit, creating as many commits as you have non-wip commits.&lt;br&gt;
For instance, with that commit history&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;. &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;br&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;. Add authentication middleware&lt;br&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;. Wip&lt;br&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;. wip&lt;br&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;. Add error message when people are not logged &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;. wIp&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;git wipsquash 1&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;will produce&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;. &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.&lt;br&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;. Add authentication middleware&lt;br&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;. Add error message when people are not logged &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;with the right commits squashed in the right place.&lt;/p&gt;
&lt;h2 id=&quot;a-little-aside-on-keeping-a-history-of-all-the-small-changes&quot; tabindex=&quot;-1&quot;&gt;A little aside on keeping a history of all the small changes&lt;/h2&gt;
&lt;p&gt;Sure, we are losing all the small steps that we took during the implementation.&lt;br&gt;
Some teams like to keep them, others don’t.&lt;br&gt;
My opinion about that changed a few times.&lt;br&gt;
At the moment, it is that I like having the small commits while I’m working on the feature (and this is why I would like to automate the naming and not just go with &lt;code&gt;wip&lt;/code&gt;), but I’m not sure of the value of keeping them after that.&lt;/p&gt;
&lt;h2 id=&quot;getting-wipsquash&quot; tabindex=&quot;-1&quot;&gt;Getting &lt;code&gt;wipsquash&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Copy the script below into a file named &lt;code&gt;git-wipsquash&lt;/code&gt; in a directory in your &lt;code&gt;PATH&lt;/code&gt;, and give it execution right with &lt;code&gt;chmod +x git-wipsquash&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;As you can see, the scripts actually runs a &lt;code&gt;git rebase -i’ and makes a lot of &lt;/code&gt;fixup` commits.&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/sh&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# git-wipsquash: Rewrite the commit history on the current branch so that&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# any commit containing &quot;wip&quot; in its message is squashed into the nearest&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# preceding commit that does NOT contain &quot;wip&quot;.&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;START_COMMIT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# If no start commit is given, find the most recent commit that doesn’t contain &quot;wip&quot; in the message.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$START_COMMIT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# Walk from HEAD backwards, find the first (i.e., newest) commit without &quot;wip&quot;.&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-list HEAD&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;SUBJECT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; log &lt;span class=&quot;token parameter variable&quot;&gt;-1&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--pretty&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;%s &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$C&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SUBJECT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-iq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;wip&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token assign-left variable&quot;&gt;START_COMMIT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$C&lt;/span&gt;^&quot;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token builtin class-name&quot;&gt;break&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$START_COMMIT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;No commit found without &#39;wip&#39; in its message. Aborting.&quot;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$START_COMMIT&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# 1) Make sure START_COMMIT is an ancestor of HEAD.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; merge-base --is-ancestor &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$START_COMMIT&lt;/span&gt;&quot;&lt;/span&gt; HEAD&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ERROR: &lt;span class=&quot;token variable&quot;&gt;$START_COMMIT&lt;/span&gt; is not an ancestor of HEAD. Aborting.&quot;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# 2) Collect commits (oldest first) strictly after START_COMMIT up to HEAD.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;COMMITS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-list &lt;span class=&quot;token parameter variable&quot;&gt;--reverse&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${START_COMMIT}&lt;/span&gt;..HEAD&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# 2) Build the interactive rebase instruction list&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;INSTRUCTIONS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;C&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$COMMITS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;SUBJECT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; log &lt;span class=&quot;token parameter variable&quot;&gt;-1&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--pretty&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;%s &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$C&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SUBJECT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-iq&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;wip&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# squash into the previous picked commit using fixup to avoid prompting a message&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;INSTRUCTIONS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$INSTRUCTIONS&lt;/span&gt;&lt;br&gt;f &lt;span class=&quot;token variable&quot;&gt;$C&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# pick normal commits&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;INSTRUCTIONS&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$INSTRUCTIONS&lt;/span&gt;&lt;br&gt;p &lt;span class=&quot;token variable&quot;&gt;$C&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# 3) Write the instructions to a temp file&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;TMPFILE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;mktemp&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# The leading newline in $INSTRUCTIONS can confuse &#39;git rebase -i&#39;, so trim it:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$INSTRUCTIONS&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;sed&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;/^[[:space:]]*$/d&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TMPFILE&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# 4) Run interactive rebase:&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Rebase the listed commits on top of START_COMMIT&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# so everything after START_COMMIT is subject to rewriting.&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;GIT_SEQUENCE_EDITOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat &lt;span class=&quot;token variable&quot;&gt;$TMPFILE&lt;/span&gt; &gt;&quot;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rebase &lt;span class=&quot;token parameter variable&quot;&gt;-i&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$START_COMMIT&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# 5) Clean up&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TMPFILE&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’re ready to go.&lt;/p&gt;
&lt;p&gt;Also, you might find it interesting to create an alias for quickly creating git wip commits.&lt;br&gt;
Add the following alias to your &lt;code&gt;.gitconfig&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-shell&quot;&gt;&lt;code class=&quot;language-shell&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;alias&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;cwip &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &quot;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; ‘wip’ --no-verify”&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can now type &lt;code&gt;git cwip&lt;/code&gt; to add all your changes and create a &lt;code&gt;wip&lt;/code&gt; commit.&lt;br&gt;
Because this adds all your changes you want to run it often, to know what you are commit without the need to review it.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;git wipsquash&lt;/code&gt; seems to solve my issue with super small commits and keep a global shared commit history clean. It’s a simple yet powerful tool to automate tedious commit cleanup, that would otherwise take too long.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>How test frameworks work : Cleaning after a failure, the birth of TearDown and SetUp</title>
      <link href="https://blog.charlesdesneuf.com/articles/how-test-frameworks-work-cleaning-after-a-failure-the-birth-of-teardown-and-setup/"/>
      <updated>2025-03-31T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/how-test-frameworks-work-cleaning-after-a-failure-the-birth-of-teardown-and-setup/</id>
      <summary>Understand how test frameworks evolved to handle cleanup after failures, leading to the creation of TearDown and SetUp methods for reliable test execution.</summary>
      <content type="html">&lt;p&gt;This is part 3 of the creation of our mental model of the internal of test frameworks.&lt;br&gt;
So far, we’ve seen that &lt;a href=&quot;/articles/how-test-frameworks-work-a-basic-mental-model/&quot;&gt;test frameworks can be seen as loops over functions&lt;/a&gt;, and that the mechanism to know that something bad occurred is to throw an exception. To prevent the exception from stopping the loop, it contains a try/catch block. This is the basics.&lt;br&gt;
In the second part, we’ve looked at how they manage to allow us &lt;a href=&quot;/articles/how-test-frameworks-work-a-basic-mental-model-expecting-exception/&quot;&gt;testing that an exception is thrown by the system under test&lt;/a&gt;. This is not as easy as it seems.&lt;/p&gt;
&lt;p&gt;In this (long awaited) third part, we will see why the tear down method is far more important than the setup method - even if we actually rarely use it.&lt;/p&gt;
&lt;p&gt;Let’s refresh our memory and see what our framework currently looks like:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;tests &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;test4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		expectedException &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; MySuperException&lt;br&gt;&lt;br&gt;		throws MySuperException&lt;br&gt;	&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;&lt;br&gt;failedTestExceptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Test loop&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tests &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; test&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	expectedException &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;null&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;exception&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exception &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; expectedException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;			failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; exception&lt;br&gt;		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;continue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Did we get our expected exception?&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expectedException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;NeverReceivedExpectedException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;expectedException&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;// Error display&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;failedTestExceptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;displayGreenBar&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token keyword&quot;&gt;foreach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;failedTestExceptions &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;displayErrorFor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;exception&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h1 id=&quot;why-do-we-need-a-teardown-method%3F&quot; tabindex=&quot;-1&quot;&gt;Why do we need a TearDown method?&lt;/h1&gt;
&lt;p&gt;Let’s see two examples to understand why a TearDown method is needed.&lt;/p&gt;
&lt;h2 id=&quot;example-1%3A-the-unclosed-connection&quot; tabindex=&quot;-1&quot;&gt;Example 1: The unclosed connection&lt;/h2&gt;
&lt;p&gt;Imagine that we have a test that requires a connection to a database.&lt;br&gt;
See how we thought about everything: because we don’t want to keep connections open, we close it at the end of the test.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;testWithConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;openConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Do stuff&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;assertSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;closeConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, what happens when the test fails? This can be either because the assertion reports an error or because the code is just plain failing and throws an exception.&lt;br&gt;
Because of the exception, the test doesn’t get to reach the end of the function, and the connection is never closed. If you have multiple tests failing in that way and you can exhaust your database connection pool. Leading to even more tests failing. That’s bad for test independence. The failure of a test can cause the failure of multiple others.&lt;/p&gt;
&lt;p&gt;Apart from database connections, another example is opened files. While the issue might not lead to a failure of multiple tests, it can lead to your test suite running slower.&lt;/p&gt;
&lt;h2 id=&quot;example-2%3A-the-interdependence-via-a-shared-resource&quot; tabindex=&quot;-1&quot;&gt;Example 2: The interdependence via a shared resource&lt;/h2&gt;
&lt;p&gt;In this example, let’s talk about 2 tests.&lt;/p&gt;
&lt;p&gt;The first test adds a burrito to the collection, does something with it, and, because it’s a nice citizen, cleans the mess at the end and removes the added burrito from the collection. Here, imagine that the collection is something we share across multiple tests, like a database.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;addBurritoToCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Swiss Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Do something with the burrito and make an assertion&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;removeBurritoFromCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Swiss Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The second test also deals with burritos.&lt;br&gt;
Maybe it’s a test about counting the number of burritos.&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;addBurritoToCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;French Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;addBurritoToCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Mexican Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;assertEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;countOfBurritos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;removeBurritoFromCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;French Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;removeBurritoFromCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Mexican Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Both tests are cleaning after themselves.&lt;/p&gt;
&lt;p&gt;Now, what if test1 starts failing for some reason? The Swiss Burrito stays in the collection because the test never reaches the last line where it was supposed to remove it.&lt;br&gt;
Test2 starts, adds two more burritos, makes its assertion, and fails. Now the collection contains 3 burritos. Swiss, French, and Mexican.&lt;/p&gt;
&lt;p&gt;We clearly see how the failure of a test impacts another one. Our tests aren’t completely independent. This might not seem like a big problem, but now imagine that you have more than 2 tests, and that you don’t run them that often.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; You are in trouble, you need to find the cause of the issue, you look in the code, possibly starting from a test that would probably continue to be ok if the other test wasn’t failing. You look around, everything seems ok. You might not even touched at that part of the code. WTF?&lt;/p&gt;
&lt;p&gt;In both examples we’ve seen that even with good intentions and cleaning in the test function, if a test fails we can run into some problems.&lt;/p&gt;
&lt;h1 id=&quot;introducing-the-teardown-method&quot; tabindex=&quot;-1&quot;&gt;Introducing the tearDown method&lt;/h1&gt;
&lt;p&gt;The TearDown mechanism is here to answer that problem. The tearDown provides a way to act after the test, even in case of failure.&lt;/p&gt;
&lt;p&gt;To do so, we need to register what we want to do after the test.&lt;/p&gt;
&lt;p&gt;In our simplified test framework, let’s add a variable that can be overridden by every test. This is where that simplified test framework starts to diverge a lot from the ones you probably are used to. In your usual test framework, tearDown (and setUp) are shared by a group of tests. To add that to the model would require to start creating a sort of collection of tests, and this is not that interesting for the mental model that I want to share.&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;// Test loop&lt;br&gt;&lt;br&gt;foreach(tests as test) {&lt;br&gt;	&lt;br&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;   tearDown = null&lt;br&gt;&lt;/span&gt;&lt;/span&gt;	expectedException = null&lt;br&gt;	try {&lt;br&gt;		test()&lt;br&gt;	} catch(exception) {&lt;br&gt;		if(exception != expectedException) {&lt;br&gt;			failedTestExceptions[] = exception&lt;br&gt;		}&lt;br&gt;		continue;&lt;br&gt;	}&lt;br&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;   finally {&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;       if(tearDown) {&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;		    tearDown()&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;	    }&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;}&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;   	&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;   // Did we get our expected exception?&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;   if(expectedException) {&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;       failedTestExceptions[] = NeverReceivedExpectedException(expectedException)&lt;br&gt;&lt;/span&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt;   }&lt;br&gt;&lt;/span&gt;&lt;/span&gt;}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we can rewrite our two examples to use the tearDown. For the connection:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token function&quot;&gt;testWithConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	tearDown &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;closeConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;	&lt;br&gt;	connection &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;openConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Do stuff&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;assertSomething&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And for the burrito collection:&lt;/p&gt;
&lt;pre class=&quot;language-php&quot;&gt;&lt;code class=&quot;language-php&quot;&gt;&lt;span class=&quot;token function&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	tearDown &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;removeBurritoFromCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Swiss Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;addBurritoToCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Swiss Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token comment&quot;&gt;// Do something with the burrito and make an assertion&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;test2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;	tearDown &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;removeBurritoFromCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;French Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;		&lt;span class=&quot;token function&quot;&gt;removeBurritoFromCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Mexican Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;	&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;addBurritoToCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;French Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;addBurritoToCollection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string single-quoted-string&quot;&gt;&#39;Mexican Burrito&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;	&lt;span class=&quot;token function&quot;&gt;assertEqual&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;countOfBurritos&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, in case of failure, the connection is properly closed, and the burritos added to the collection are removed. Thank you, tearDown. Our tests are way more independent than before.&lt;/p&gt;
&lt;h1 id=&quot;my-theory-about-teardown-and-setup-methods&quot; tabindex=&quot;-1&quot;&gt;My theory about tearDown and setup methods&lt;/h1&gt;
&lt;p&gt;&lt;strong&gt;This is why the tearDown method is actually more important than the setUp one. The tearDown is needed, while the setUp is just something convenient to factorize some common test setup in one place and avoid repetition.&lt;/strong&gt; Which is nice too but not as crucial.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;My theory is that the tearDown was discovered as something we needed for test independence, to make sure we properly clean after each test and that adding setUp was just a question of symmetry.&lt;/strong&gt; After all, if I have a tearDown shared by all tests, why not have something that helps me setUp all my tests? Also, if we clean the connection after the test, wouldn’t it make sense to be able to create it before the test&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Since I started that series of articles long ago I started sharing carousels on LinkedIn about tests, and shared one about what this series talks about. Someone commented that Kent Beck demonstrates how to create JUnit in TDD in his book &lt;em&gt;TDD By Example&lt;/em&gt;.  This is a fun example because it shows how to create a test framework in TDD when you don’t have a test framework yet, and to create something using itself.  Anyway, I totally forgot that Kent did that in the book and grabbed my copy to see what he was saying. With the steps he takes, the setUp method is introduced before the tearDown one. This contradicts my theory but this might only be because Kent already knew which form the test framework will have in the end. And to add some credit to my theory, the chapter is called &amp;quot;Cleaning up after&amp;quot; and the introduction is&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Sometimes tests need to allocate external resources in setUp(). &lt;strong&gt;If we want the tests to remains independent, then a test that allocates external resources needs to release them before it is done, perhaps in a tearDown() method.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I find the formulation strange, because I don’t think that tests &lt;strong&gt;need&lt;/strong&gt; to allocate external resources in setUp(), they could really do it inside the test, but for sure, they do need to release external resources no matter what.&lt;/p&gt;
&lt;p&gt;So, here it is, end of part 3. We’ve seen how and why the tearDown method came to be and how important it is. Always remember to clean after your test if you use something that can mess with other tests or that can cause issues in the system.&lt;/p&gt;
&lt;p&gt;Hey, &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let’s have a chat!&lt;/a&gt;.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Taking smaller steps would reduce the difficulty of dealing with that problem.&lt;br&gt;
You see that the tests are failing, you revert to the previous good state, and you start again. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;My examples are probably itching some of you.&lt;br&gt;
How can the closeConnection function have access to the connection variable that seems to be declared in the test? You are right, that code probably doesn’t work, but is good enough for building a mental model.&lt;br&gt;
Sorry, not sorry :) &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Quickly open Google Calendar Tab in Google Chrome from Alfred</title>
      <link href="https://blog.charlesdesneuf.com/articles/quickly-open-google-calendar-tab-in-google-chrome-from-alfred/"/>
      <updated>2025-07-04T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/quickly-open-google-calendar-tab-in-google-chrome-from-alfred/</id>
      <summary>Just type a few letters in Alfred command palette and go to your emails and calendar in a pinned Google Chrome tab.</summary>
      <content type="html">&lt;p&gt;I have pinned tabs for Calendar and Gmail, and I wanted to quickly jump to them from anywhere using Alfred.&lt;/p&gt;
&lt;p&gt;Another option could have been to use Alfred’s Web Search feature to open a new tab. That would be a simple version of what I wanted, but I was curious to see if it was possible to go to an existing tab.&lt;/p&gt;
&lt;p&gt;Additionally, Alfred offers another option with an Automation Task accessible in the Workflow menu (&lt;code&gt;Automation &amp;gt; Automation Task &amp;gt; Extra Automation Task &amp;gt; Web Browser&amp;gt; Chromium &amp;gt; Switch to Chromium Browser Tab Matching URL&lt;/code&gt;). I discovered that solution after implementing mine, but I decided to keep what I had because the Automation Task doesn’t properly handle having multiple Chrome windows opened, and I usually have several of them.&lt;/p&gt;
&lt;p&gt;In the end, my solution is an Alfred Workflow I made that runs an AppleScript&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt; when a keyword is entered in the Alfred command palette.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;workflow.png&quot; alt=&quot;Alfred Workflow&quot;&gt;&lt;/p&gt;
&lt;p&gt;The Keyword trigger configuration is simple: Take the keyword &amp;quot;calendar&amp;quot; with no argument. then make it look nice by adding a title, a subtext and Google Calendar icon.&lt;br&gt;
&lt;img src=&quot;keyword.png&quot; alt=&quot;Alfred Keyword window&quot;&gt;&lt;/p&gt;
&lt;p&gt;Add an AppleScript action with the following script:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;on &lt;span class=&quot;token function&quot;&gt;alfred_script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;q&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt; tell application &lt;span class=&quot;token string&quot;&gt;&quot;Google Chrome&quot;&lt;/span&gt;&lt;br&gt;	activate&lt;br&gt;	&lt;br&gt;	&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt; Loop through all windows&lt;br&gt;	repeat &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; windowIndex from &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; to count &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; windows&lt;br&gt;		&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; theWindow to window windowIndex&lt;br&gt;		&lt;br&gt;		&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt; Loop through all tabs &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; the current window&lt;br&gt;		repeat &lt;span class=&quot;token keyword&quot;&gt;with&lt;/span&gt; tabIndex from &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; to count &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; tabs &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; theWindow&lt;br&gt;			&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; currentTab to tab tabIndex &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; theWindow&lt;br&gt;			&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; tabURL to &lt;span class=&quot;token constant&quot;&gt;URL&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; currentTab&lt;br&gt;			&lt;br&gt;			&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt; Check &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; the tab contains Google Calendar&lt;br&gt;			&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; tabURL contains &lt;span class=&quot;token string&quot;&gt;&quot;calendar.google.com&quot;&lt;/span&gt; then&lt;br&gt;				&lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt; Make &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt; window active and &lt;span class=&quot;token keyword&quot;&gt;switch&lt;/span&gt; to the calendar tab&lt;br&gt;				&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; index &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; theWindow to &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br&gt;				&lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt; active tab index &lt;span class=&quot;token keyword&quot;&gt;of&lt;/span&gt; theWindow to tabIndex&lt;br&gt;				&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;--&lt;/span&gt; Exit the script once found&lt;br&gt;			end &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt;&lt;br&gt;		end repeat&lt;br&gt;	end repeat&lt;br&gt;  end tell&lt;br&gt;end alfred_script&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For an extra boost of productivity&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;, make it so the calendar opens on the weekly view for today. For this, you can modify the script to ask Chrome to change the tab URL:&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt; set active tab index of theWindow to tabIndex&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token line&quot;&gt; set URL of currentTab to &quot;https://calendar.google.com/calendar/u/0/r/week&quot;&lt;br&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token line&quot;&gt; return -- Exit the script once found&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I also created a similar script for Gmail by using another Keyword trigger and using Gmail&#39;s url.&lt;/p&gt;
&lt;p&gt;Now I can access my calendar and emails from anywhere!&lt;/p&gt;
&lt;p&gt;Hey, &lt;a href=&quot;https://calendar.google.com/calendar/u/0/appointments/schedules/AcZssZ1JNUIOxz7VeexstXYhJ2mSl9XM62HUs37E22xu90EWft9Iu-bm1cFT_gqCx4_pEGsw7s7B5P3h&quot;&gt;let’s have a chat!&lt;/a&gt;.&lt;/p&gt;
&lt;hr class=&quot;footnotes-sep&quot;&gt;
&lt;section class=&quot;footnotes&quot;&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;
&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Actually, Claude made the AppleScript. I still don’t understand how this language works, and didn’t take the time to get it. &lt;a href=&quot;#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Lol &lt;a href=&quot;#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Testing Rule of Thumb: if it looks too much like the real implementation, doubt it</title>
      <link href="https://blog.charlesdesneuf.com/articles/testing-rule-of-thumb-if-it-looks-too-much-like-the-real-implementation-doubt-it/"/>
      <updated>2025-07-16T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/testing-rule-of-thumb-if-it-looks-too-much-like-the-real-implementation-doubt-it/</id>
      <summary>If a test looks like it’s doing calculation or is using the same code as the implementation code it might not be helping you as much as you think.</summary>
      <content type="html">&lt;p&gt;This can mean a variety of things.&lt;/p&gt;
&lt;p&gt;If you have logic in tests or are doing some calculations be suspicious, especially if this is for the expected value of the assertion. Prefer literal values or variables without any manipulation there.&lt;/p&gt;
&lt;p&gt;Another good example is complex mock setup. &lt;strong&gt;If you are writing a complex matcher that is a mirror of what you need to write in the implementation you are not testing anything&lt;/strong&gt;, you are painting by number, but you are the one making the template. Don’t be surprised if the implementation doesn’t do what you expect it to. The only solution here is to introduce some tests that really exercise the thing you mock, following the &lt;strong&gt;Don’t mock what you don’t own advice&lt;/strong&gt;. The classical example of this is people mocking ORMs and writing their queries in the tests and in the implementation. The double the work but still no guarantee that your DB will actually do what you want it to do.&lt;/p&gt;
&lt;p&gt;One final example is validating an external message using its internal representation. Here think about APIs schema. You shouldn’t use your API response DTO to construct the expected API response. The test is supposed to validate a contract. &lt;strong&gt;If that contract moves when you are modifying the code it isn’t of any help.&lt;/strong&gt; Imagine that you rename a field in your DTO using an automated refactoring tool. Shouldn’t your test fail and tells you that you are breaking the contract established with your API clients? If you use the same DTO the test will continue passing as both the implementation and the test will be modified. If you don’t preserve the behavior, this is not refactoring. Write your tests in a way that they tell you if are changing behavior: do not build your expectation the same way you are building the actual value.&lt;/p&gt;
&lt;p&gt;As you see, if your test looks too much like the implementation, there is possibly something shady.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Test Migration: From Mocks to Real Database with Test Switcher</title>
      <link href="https://blog.charlesdesneuf.com/articles/test-migration-from-mocks-to-real-database-with-test-switcher/"/>
      <updated>2025-08-25T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/test-migration-from-mocks-to-real-database-with-test-switcher/</id>
      <summary>Master test migration from ORM mocks to real databases using the Test Switcher pattern for gradual, risk-free refactoring without breaking CI.</summary>
      <content type="html">&lt;h1 id=&quot;gradual-database-migration-strategy%3A-from-mocks-to-real-database&quot; tabindex=&quot;-1&quot;&gt;Gradual Database Migration Strategy: From Mocks to Real Database&lt;/h1&gt;
&lt;p&gt;Last week, I needed to migrate a test suite from Prisma mocks to a real database to improve test reliability. In short, using mocks of your ORM is one of the worst things you can do to your test suite. They give you false confidence and make your tests hard to read and maintain.&lt;br&gt;
So we wanted the mocks out, and I was tasked by my team to do that work.&lt;br&gt;
However, I wanted to avoid the chaos of having all tests fail simultaneously when switching implementations. Instead, I opted for a gradual, test-by-test migration approach.&lt;/p&gt;
&lt;h3 id=&quot;the-strategy&quot; tabindex=&quot;-1&quot;&gt;The Strategy&lt;/h3&gt;
&lt;p&gt;Here’s the step-by-step approach I used:&lt;/p&gt;
&lt;h4 id=&quot;1.-create-the-interface-layer&quot; tabindex=&quot;-1&quot;&gt;1. Create the Interface Layer&lt;/h4&gt;
&lt;p&gt;Fortunately, we had a repository class encapsulating the calls to the ORM. My first move was to add an interface on top of the existing repository to abstract the implementation details.&lt;/p&gt;
&lt;h4 id=&quot;2.-build-the-test-switcher&quot; tabindex=&quot;-1&quot;&gt;2. Build the Test Switcher&lt;/h4&gt;
&lt;p&gt;I created a new implementation called &lt;code&gt;TestSwitcherXXXRepo&lt;/code&gt; that acts as a proxy between the two instances of the original repository.&lt;br&gt;
It takes two parameters: one for the repository instance using the mocked ORM and one instance using the real database.&lt;/p&gt;
&lt;p&gt;The proxy contains an &lt;code&gt;instance&lt;/code&gt; property that defaults to the mocked implementation. By default, we will keep using the mocked version, which is the one in use for the tests in the current state.&lt;/p&gt;
&lt;p&gt;The proxy also offers a &lt;code&gt;switchToDatabase()&lt;/code&gt; method to toggle to the real database implementation.&lt;/p&gt;
&lt;p&gt;Here is an example of a test switcher:&lt;/p&gt;
&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/*This class is not meant to stay. This is something temporary to allow for switching the implementation test by test */&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TestSwitcherPonyCareRepository&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PonyCareRepository&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; instance&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PonyCareRepository&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; mockPrismaPonyCareRepository&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PrismaPonyCareRepository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; realPrismaPonyCareRepository&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PrismaPonyCareRepository&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mockPrismaPonyCareRepository&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token function&quot;&gt;switchToRealDatabase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;realPrismaPonyCareRepository&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;fetchCareSchedules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;    stableId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StableId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;    ponyId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PonyId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;PonyCareSchedule&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetchCareSchedules&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;stableId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ponyId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;saveCareUpdates&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;br&gt;    careScheduleToSave&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PonyCareScheduleToSave&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;Promise&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;instance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;saveCareUpdates&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;careScheduleToSave&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, I’m lucky and working on a pony club management application.&lt;br&gt;
(Unfortunately, that’s not true, and the original repository was about a less glamorous topic.)&lt;/p&gt;
&lt;p&gt;Creating a proxy is an easy task using the refactoring tools that good IDEs have to offer.&lt;/p&gt;
&lt;h4 id=&quot;3.-set-up-the-test-infrastructure&quot; tabindex=&quot;-1&quot;&gt;3. Set Up the Test Infrastructure&lt;/h4&gt;
&lt;p&gt;Now it’s time to start modifying the tests.&lt;br&gt;
The first thing to do is instantiate the &lt;code&gt;TestSwitcherXXXRepo&lt;/code&gt; with both the mock and real database repositories.&lt;/p&gt;
&lt;p&gt;The next thing is to replace the usage of the original repository with the switcher implementation.&lt;/p&gt;
&lt;p&gt;This setup took only a few minutes. The trickiest part was remembering how to select specific interface implementations in NestJS.&lt;/p&gt;
&lt;h4 id=&quot;4.-migrate-tests-incrementally&quot; tabindex=&quot;-1&quot;&gt;4. Migrate Tests Incrementally&lt;/h4&gt;
&lt;p&gt;We now have the infrastructure in place to switch each test one by one.&lt;/p&gt;
&lt;p&gt;For each test, start by adding &lt;code&gt;testSwitcher.switchToRealDatabase()&lt;/code&gt; as the first line. This also serves as a clear marker of migrated tests.&lt;/p&gt;
&lt;p&gt;Then, set up the necessary database fixtures and update the assertions to work with real data instead of mock expectations.&lt;/p&gt;
&lt;p&gt;This step was the most time-consuming. I had to reverse-engineer each test to understand what data it needed to demonstrate the expected behavior.&lt;/p&gt;
&lt;p&gt;This becomes significantly more challenging with complex database queries—if you have intricate queries, consider testing them at the repository level instead of mixing them with testing business behavior as well.&lt;/p&gt;
&lt;h4 id=&quot;5.-clean-up&quot; tabindex=&quot;-1&quot;&gt;5. Clean Up&lt;/h4&gt;
&lt;p&gt;Once all tests are migrated, you can inject the real database repository directly, remove all &lt;code&gt;switchToDatabase()&lt;/code&gt; calls, and delete the &lt;code&gt;TestSwitcherXXXRepo&lt;/code&gt; implementation.&lt;/p&gt;
&lt;h3 id=&quot;benefits&quot; tabindex=&quot;-1&quot;&gt;Benefits&lt;/h3&gt;
&lt;p&gt;This approach has several benefits:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can fix tests one at a time, with only a single test failing at any given moment&lt;/li&gt;
&lt;li&gt;We are able to push changes after each test migration, keeping CI green and enabling continuous merging&lt;/li&gt;
&lt;li&gt;We maintained a working test suite throughout the migration process&lt;/li&gt;
&lt;li&gt;We easily tracked migration progress through the &lt;code&gt;switchToDatabase()&lt;/code&gt; markers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The gradual migration strategy proved to be efficient for our migration. At no point were we lost with a lot of tests failing, fearing that we would face a massive merge conflict while other team members could be working on that same test file. As often happens, the key to migrating code is to create the tools to make it a less stressful experience.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Solving Obsidian + Readwise Merge Conflicts with a Custom Git Driver</title>
      <link href="https://blog.charlesdesneuf.com/articles/solving-obsidian-readwise-merge-conflicts-with-a-custom-git-driver/"/>
      <updated>2026-01-12T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/solving-obsidian-readwise-merge-conflicts-with-a-custom-git-driver/</id>
      <summary>Learn how to create a custom Git merge driver to automatically resolve Obsidian Readwise plugin conflicts. Includes Bash testing with Bats and full code.</summary>
      <content type="html">&lt;p&gt;If you&#39;re using Obsidian with a Git-based backup system and the Readwise plugin, you&#39;ve probably encountered this frustrating scenario: you sync your vault on two different machines, both have new&lt;br&gt;
highlights from Readwise, and suddenly you&#39;re staring at merge conflicts in a JSON file you never intended to edit manually.&lt;/p&gt;
&lt;p&gt;I got tired of resolving these conflicts by hand. So I asked Claude Code to help me build a solution.&lt;/p&gt;
&lt;h2 id=&quot;the-problem&quot; tabindex=&quot;-1&quot;&gt;The Problem&lt;/h2&gt;
&lt;p&gt;My Obsidian vault is synced across multiple devices using Git. The Readwise plugin stores its state in &lt;code&gt;.obsidian/plugins/readwise-official/data.json&lt;/code&gt;, which contains:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;lastSavedStatusID&lt;/code&gt; field tracking the sync progress&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;booksIDsMap&lt;/code&gt; object mapping file paths to Readwise book IDs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When I read articles on my phone and sync Readwise on my laptop, then do the same on my desktop, Git sees two divergent versions of this file. The result? Merge conflicts with those dreaded &lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&lt;/code&gt;&lt;br&gt;
markers in a JSON file.&lt;/p&gt;
&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; HEAD&lt;br&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;lastSavedStatusID&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;23934436&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;=======&lt;br&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;lastSavedStatusID&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;23924421&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;br&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; other-branch&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fix is obvious to a human: keep the highest ID (it represents the most recent sync) and merge the book mappings. But doing this manually every time is just plain annoying.&lt;/p&gt;
&lt;h2 id=&quot;the-solution%3A-custom-git-merge-drivers&quot; tabindex=&quot;-1&quot;&gt;The Solution: Custom Git Merge Drivers&lt;/h2&gt;
&lt;p&gt;Git has a lesser-known feature called &lt;strong&gt;custom merge drivers&lt;/strong&gt;. Instead of showing conflict markers, Git can call a custom script to resolve conflicts automatically.&lt;/p&gt;
&lt;p&gt;I described my problem to Claude Code: &amp;quot;I want a merge rule for &lt;code&gt;readwise-official/data.json&lt;/code&gt;. In case of conflict, always keep the highest ID.&amp;quot;&lt;/p&gt;
&lt;p&gt;Claude Code immediately understood the requirement and created a solution using &lt;code&gt;jq&lt;/code&gt; for proper JSON manipulation. When I later realized the &lt;code&gt;booksIDsMap&lt;/code&gt; also needed merging, Claude updated the script&lt;br&gt;
accordingly.&lt;/p&gt;
&lt;h2 id=&quot;testing-first-with-bats&quot; tabindex=&quot;-1&quot;&gt;Testing First with Bats&lt;/h2&gt;
&lt;p&gt;I didn&#39;t want to blindly trust a script that modifies my data during merges. I asked Claude Code to write tests before we finalized the implementation.&lt;/p&gt;
&lt;p&gt;Claude chose &lt;a href=&quot;https://github.com/bats-core/bats-core&quot;&gt;Bats&lt;/a&gt; (Bash Automated Testing System), a testing framework for Bash scripts.&lt;br&gt;
This was a good use case to finally get started with bats.&lt;/p&gt;
&lt;p&gt;Here&#39;s what the test structure looks like:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function-name function&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;TEST_DIR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;mktemp &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;SCRIPT_DIR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$BATS_TEST_DIRNAME&lt;/span&gt;/..&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;BASE_JSON&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&#39;{&lt;br&gt;  &quot;token&quot;: &quot;test-token&quot;,&lt;br&gt;  &quot;lastSavedStatusID&quot;: %s,&lt;br&gt;  &quot;booksIDsMap&quot;: {%s}&lt;br&gt;}&#39;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token function-name function&quot;&gt;teardown&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-rf&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token function-name function&quot;&gt;create_json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;status_id&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;books_map&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$2&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$BASE_JSON&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$status_id&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$books_map&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was surprised, in a good way, to see that Claude thought about using fixture builder like the &lt;code&gt;create_json&lt;/code&gt; function.&lt;/p&gt;
&lt;p&gt;It helped creating some nice tests. Here are a few examples:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;@test &lt;span class=&quot;token string&quot;&gt;&quot;lastSavedStatusID: takes other when other &gt; current&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    create_json &lt;span class=&quot;token string&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/ancestor.json&quot;&lt;/span&gt;&lt;br&gt;    create_json &lt;span class=&quot;token string&quot;&gt;&quot;150&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/current.json&quot;&lt;/span&gt;&lt;br&gt;    create_json &lt;span class=&quot;token string&quot;&gt;&quot;200&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/other.json&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    run &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SCRIPT_DIR&lt;/span&gt;/readwise-merge.sh&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/ancestor.json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/current.json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/other.json&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$status&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&quot;lastSavedStatusID&quot;: [0-9]*&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/current.json&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;grep&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;[0-9]*&#39;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$result&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;@test &lt;span class=&quot;token string&quot;&gt;&quot;booksIDsMap: no data loss - all unique entries from both sides are preserved&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;    create_json &lt;span class=&quot;token string&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/ancestor.json&quot;&lt;/span&gt;&lt;br&gt;    create_json &lt;span class=&quot;token string&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&quot;current1.md&quot;: &quot;1&quot;, &quot;current2.md&quot;: &quot;2&quot;, &quot;shared.md&quot;: &quot;100&quot;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/current.json&quot;&lt;/span&gt;&lt;br&gt;    create_json &lt;span class=&quot;token string&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&quot;other1.md&quot;: &quot;3&quot;, &quot;other2.md&quot;: &quot;4&quot;, &quot;shared.md&quot;: &quot;999&quot;&#39;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/other.json&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    run &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$SCRIPT_DIR&lt;/span&gt;/readwise-merge.sh&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/ancestor.json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/current.json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;br&gt;        &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/other.json&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$status&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# Should have exactly 5 unique entries&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;jq &lt;span class=&quot;token string&quot;&gt;&#39;.booksIDsMap | length&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/current.json&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$count&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token comment&quot;&gt;# Shared key should have current&#39;s value, not other&#39;s&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;shared_value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;jq &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;.booksIDsMap[&quot;shared.md&quot;]&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$TEST_DIR&lt;/span&gt;/current.json&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$shared_value&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;100&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Running &lt;code&gt;bats tests/readwise-merge.bats&lt;/code&gt; gives us confidence that the driver behaves correctly:&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt; ok 1 lastSavedStatusID: keeps current when current &gt; other&lt;br&gt; ok 2 lastSavedStatusID: takes other when other &gt; current&lt;br&gt; ok 3 lastSavedStatusID: keeps current when equal&lt;br&gt; ok 4 booksIDsMap: keeps current entries when other has none&lt;br&gt; ok 5 booksIDsMap: adds entries from other that current doesn&#39;t have&lt;br&gt; ok 6 booksIDsMap: merges entries from both current and other&lt;br&gt; ok 7 booksIDsMap: no duplicates when same entry in both&lt;br&gt; ok 8 booksIDsMap: current wins when same key has different value&lt;br&gt; ok 9 booksIDsMap: no data loss - all unique entries from both sides are preserved&lt;br&gt; ok 10 combined: handles both lastSavedStatusID and booksIDsMap&lt;br&gt; ok 11 output is valid JSON&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-merge-driver&quot; tabindex=&quot;-1&quot;&gt;The Merge Driver&lt;/h2&gt;
&lt;p&gt;Here&#39;s the final script:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Custom merge driver for readwise-official/data.json&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# - Keeps the highest lastSavedStatusID&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# - Merges booksIDsMap (current wins on conflicts)&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Arguments: %O %A %B (ancestor, current, other)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;ANCESTOR&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;CURRENT&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$2&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;OTHER&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$3&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Extract values using jq&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;CURRENT_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;jq &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;.lastSavedStatusID // 0&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CURRENT&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;OTHER_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;jq &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;.lastSavedStatusID // 0&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$OTHER&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Determine highest ID&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$OTHER_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-gt&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CURRENT_ID&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;HIGHEST_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$OTHER_ID&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;HIGHEST_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CURRENT_ID&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Merge: start with other&#39;s booksIDsMap, overlay current&#39;s (current wins on conflicts)&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Then update lastSavedStatusID with highest value&lt;/span&gt;&lt;br&gt;jq &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;&lt;br&gt;   .[0] as $current |&lt;br&gt;   .[1] as $other |&lt;br&gt;   $current |&lt;br&gt;   .lastSavedStatusID = &#39;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$HIGHEST_ID&lt;/span&gt;&quot;&lt;/span&gt;&#39; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt;&lt;br&gt;   .booksIDsMap &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$other&lt;/span&gt;.booksIDsMap // &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; + &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$current&lt;/span&gt;.booksIDsMap // &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;&#39; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CURRENT&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$OTHER&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CURRENT&lt;/span&gt;.tmp&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;mv&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CURRENT&lt;/span&gt;.tmp&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$CURRENT&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;exit&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;installation&quot; tabindex=&quot;-1&quot;&gt;Installation&lt;/h2&gt;
&lt;h3 id=&quot;1.-create-the-driver-script&quot; tabindex=&quot;-1&quot;&gt;1. Create the driver script&lt;/h3&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; .git-merge-drivers&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Save the script above as .git-merge-drivers/readwise-merge.sh&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;chmod&lt;/span&gt; +x .git-merge-drivers/readwise-merge.sh&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;2.-create-.gitattributes&quot; tabindex=&quot;-1&quot;&gt;2. Create &lt;code&gt;.gitattributes&lt;/code&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;.obsidian/plugins/readwise-official/data.json merge=readwise-highest-id&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;3.-configure-git&quot; tabindex=&quot;-1&quot;&gt;3. Configure Git&lt;/h3&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config merge.readwise-highest-id.name &lt;span class=&quot;token string&quot;&gt;&quot;Readwise merge - keep highest ID and merge books&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config merge.readwise-highest-id.driver &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;/.git-merge-drivers/readwise-merge.sh %O %A %B&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;4.-if-you-already-have-a-conflict&quot; tabindex=&quot;-1&quot;&gt;4. If you already have a conflict&lt;/h3&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; checkout &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; .obsidian/plugins/readwise-official/data.json&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; .obsidian/plugins/readwise-official/data.json&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rebase &lt;span class=&quot;token parameter variable&quot;&gt;--continue&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;# or git merge --continue&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion%3A-beyond-readwise&quot; tabindex=&quot;-1&quot;&gt;Conclusion: Beyond Readwise&lt;/h2&gt;
&lt;p&gt;This experience got me thinking about other merge pain points.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lock files.&lt;/strong&gt; Anyone who has resolved conflicts in &lt;code&gt;package-lock.json&lt;/code&gt;, &lt;code&gt;yarn.lock&lt;/code&gt;, or &lt;code&gt;composer.lock&lt;/code&gt; knows the frustration. These files are generated, not authored. The correct resolution is almost&lt;br&gt;
always &amp;quot;regenerate the file&amp;quot;—run &lt;code&gt;npm install&lt;/code&gt;, &lt;code&gt;yarn install&lt;/code&gt;, or &lt;code&gt;composer install&lt;/code&gt; after merging &lt;code&gt;package.json&lt;/code&gt;. A custom merge driver could automate this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LLM-backed merging.&lt;/strong&gt; For simple, rule-based conflicts, we could describe the merge logic in a prompt and let an LLM resolve them. &amp;quot;Always keep the higher version number.&amp;quot; &amp;quot;Merge arrays by taking the&lt;br&gt;
union.&amp;quot; &amp;quot;For this config file, prefer values from the current branch.&amp;quot; This could work for cases where the rules are clear but the file format is complex.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deterministic scripts over manual resolution.&lt;/strong&gt; The real win here is moving from ad-hoc manual fixes to &lt;strong&gt;deterministic&lt;/strong&gt; solutions. Given the same inputs, the merge driver always produces the same&lt;br&gt;
output. No human judgment required, no mistakes from rushing through conflicts at 6 PM on a Friday.&lt;/p&gt;
&lt;p&gt;The next time you find yourself resolving the same type of conflict repeatedly, consider: can you describe the resolution rules? If yes, you can probably automate it.&lt;/p&gt;
</content>
    </entry>
  
    
    <entry>
      <title>Easily use multiple git identities on one laptop</title>
      <link href="https://blog.charlesdesneuf.com/articles/easily-use-multiple-git-identities-on-one-laptop/"/>
      <updated>2026-02-01T00:00:00Z</updated>
      <id>https://blog.charlesdesneuf.com/articles/easily-use-multiple-git-identities-on-one-laptop/</id>
      <summary>Learn how to manage multiple Git identities on a single laptop using a zsh hook and fzf. Never accidentally commit with the wrong email again—perfect for consultants and developers juggling personal and work repositories.</summary>
      <content type="html">&lt;p&gt;When you&#39;re a consultant with multiple clients, or fetching personal Git repositories on your work laptop, you might need to switch between multiple identities. You might use your personal handle and email address to commit on your personal projects, and use a work-related email and full name for work repositories.&lt;/p&gt;
&lt;p&gt;And if you&#39;re in that situation, chances are high that you&#39;ve used the wrong identity in a repo at least once.&lt;/p&gt;
&lt;p&gt;This happened to me again the other day. I have some config files that reuse one multiple laptops, my work one included, and that config comes with a global git config that is set for my personal projects. Long story short, I committed with my personal info on a work repo. That&#39;s not a huge issue, but I like to keep these two things separate when possible. So, I wonder, how could I avoid that? And as often lately, I turned to Claude to find a solution.&lt;/p&gt;
&lt;p&gt;I ended up creating a zsh hook that warns when entering a git repository without local identity configuration, plus a helper function to quickly apply the right identity.&lt;/p&gt;
&lt;h2 id=&quot;the-hook&quot; tabindex=&quot;-1&quot;&gt;The hook&lt;/h2&gt;
&lt;p&gt;The following code hooks on &lt;code&gt;chpwd&lt;/code&gt; which allows to detect a change of directory, either with &lt;code&gt;cd&lt;/code&gt; or jumper utilities like &lt;code&gt;z&lt;/code&gt;/&lt;code&gt;zoxide&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;When entering a directory, it first looks to see if that is a git repository, if not, it exits.&lt;br&gt;
Otherwise it looks if an identity is configured in the local git config. If that is the first time we enter the repository for that session it will display a message:&lt;/p&gt;
&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;[git-identity] No local identity configured&lt;br&gt;Run: git-identity-apply &amp;lt;module&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will talk about &lt;code&gt;git-identity-apply&lt;/code&gt; shortly after.&lt;br&gt;
It keeps track of which repositories were seen during a session to avoid showing the message each time we &lt;code&gt;cd&lt;/code&gt; inside a repo.&lt;/p&gt;
&lt;p&gt;Here is the hook script:&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Git identity check on directory change&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Warns when entering a git repo without local identity configuration&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Track warned repos to avoid repeated warnings per session&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;typeset&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-gA&lt;/span&gt; _git_identity_warned_repos&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Find the dotfiles directory&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${DOTFILES&lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt;&quot;$HOME&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;.dotfiles&quot;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Check git identity and warn if not configured&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token function-name function&quot;&gt;_git_identity_check&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# Only proceed if we&#39;re inside a git worktree&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-parse --is-inside-work-tree &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;true&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; git_root&lt;br&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;git_root&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-parse --show-toplevel &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# Skip if we&#39;ve already warned about this repo this session&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${_git_identity_warned_repos&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;$git_root&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;}&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# Check if local identity is configured&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; local_email local_name&lt;br&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;local_email&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config &lt;span class=&quot;token parameter variable&quot;&gt;--local&lt;/span&gt; user.email &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;local_name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config &lt;span class=&quot;token parameter variable&quot;&gt;--local&lt;/span&gt; user.name &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# Warn if either is missing&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$local_email&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$local_name&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;    _git_identity_warned_repos&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;$git_root&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[git-identity] No local identity configured&quot;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;  Run: git-identity-apply &amp;lt;module&gt;&quot;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Register the chpwd hook&lt;/span&gt;&lt;br&gt;autoload &lt;span class=&quot;token parameter variable&quot;&gt;-Uz&lt;/span&gt; add-zsh-hook&lt;br&gt;add-zsh-hook chpwd _git_identity_check&lt;br&gt;&lt;br&gt;&lt;span class=&quot;token comment&quot;&gt;# Also run on shell startup in case we start in a git repo&lt;/span&gt;&lt;br&gt;_git_identity_check&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we have an easy way to know that we didn&#39;t set a local identity, but we still need to add one. This means two &lt;code&gt;git config&lt;/code&gt; commands to run, but I never remember what they are and I mostly want to use the same identities—either my personal one or a client-related one.&lt;/p&gt;
&lt;p&gt;My configuration system comes with modules. Module can be anything. I have a module for PHP that install things PHP related, one named &amp;quot;Perso&amp;quot; that installs things that I want on every laptop. I also have one, you guessed it, by company I work for. Using symlinks I can decide on each laptop which module I want to install, run a script, and I keep everything up to date using brew.&lt;/p&gt;
&lt;p&gt;So, let&#39;s go back to our git identity thing.&lt;/p&gt;
&lt;p&gt;In each module where I want a separate identity, I add a file called &lt;code&gt;git-identity&lt;/code&gt; that looks like:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config &lt;span class=&quot;token parameter variable&quot;&gt;--local&lt;/span&gt; user.email &lt;span class=&quot;token string&quot;&gt;&quot;jane.doe@acme-corp.com&quot;&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config &lt;span class=&quot;token parameter variable&quot;&gt;--local&lt;/span&gt; user.name &lt;span class=&quot;token string&quot;&gt;&quot;Jane Doe&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I made a script, the &lt;code&gt;git-identity-apply&lt;/code&gt; you&#39;ve been waiting to meet, that takes a module name as an argument and configures the repo with the matching identity. To make things even more convenient, you can run the command without parameter, the command lists all modules with an associated identity and lets you select one using &lt;code&gt;fzf&lt;/code&gt;. Select one, press enter, and boom, you&#39;re ready to go.&lt;/p&gt;
&lt;pre class=&quot;language-sh&quot;&gt;&lt;code class=&quot;language-sh&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Apply git identity from a module configuration&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token function-name function&quot;&gt;git-identity-apply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$1&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;modules_dir&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$DOTFILES&lt;/span&gt;/Modules&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# Interactive selection with fzf if no module specified&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$module&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;identity_file&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$modules_dir&lt;/span&gt;&quot;&lt;/span&gt;/*/git-identity&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;N&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token assign-left variable&quot;&gt;modules&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${identity_file&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;h&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;t}&lt;/span&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;${&lt;span class=&quot;token operator&quot;&gt;#&lt;/span&gt;modules&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;@&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;}&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-eq&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[git-identity] No modules with git-identity found&quot;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token assign-left variable&quot;&gt;module&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;printf&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;%s&#92;n&#39;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${modules&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;@&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;}&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; fzf &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token parameter variable&quot;&gt;--header&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Select git identity&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token parameter variable&quot;&gt;--preview&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;cat &lt;span class=&quot;token variable&quot;&gt;$modules_dir&lt;/span&gt;/{}/git-identity&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;&lt;br&gt;      --preview-window&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;up:3&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-z&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$module&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# Find the module&#39;s git-identity file (case-insensitive match)&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; identity_file&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token for-or-select variable&quot;&gt;dir&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$modules_dir&lt;/span&gt;&quot;&lt;/span&gt;/*&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;N&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;dir_name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${dir&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;t}&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${(L)dir_name}&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${(L)module}&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token assign-left variable&quot;&gt;identity_file&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$dir&lt;/span&gt;/git-identity&quot;&lt;/span&gt;&lt;br&gt;      &lt;span class=&quot;token builtin class-name&quot;&gt;break&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;done&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$identity_file&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[git-identity] Module &#39;&lt;span class=&quot;token variable&quot;&gt;$module&lt;/span&gt;&#39; not found or has no git-identity file&quot;&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# Source the identity file to apply the config&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;source&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$identity_file&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# Display the applied identity&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; applied_email applied_name&lt;br&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;applied_email&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config &lt;span class=&quot;token parameter variable&quot;&gt;--local&lt;/span&gt; user.email &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;applied_name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config &lt;span class=&quot;token parameter variable&quot;&gt;--local&lt;/span&gt; user.name &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;[git-identity] Applied identity from &lt;span class=&quot;token variable&quot;&gt;$module&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;  user.email = &lt;span class=&quot;token variable&quot;&gt;$applied_email&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;  user.name  = &lt;span class=&quot;token variable&quot;&gt;$applied_name&lt;/span&gt;&quot;&lt;/span&gt;&lt;br&gt;&lt;br&gt;  &lt;span class=&quot;token comment&quot;&gt;# Clear the warned flag for this repo since it&#39;s now configured&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token builtin class-name&quot;&gt;local&lt;/span&gt; git_root&lt;br&gt;  &lt;span class=&quot;token assign-left variable&quot;&gt;git_root&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token variable&quot;&gt;&lt;span class=&quot;token variable&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; rev-parse --show-toplevel &lt;span class=&quot;token operator&quot;&gt;&lt;span class=&quot;token file-descriptor important&quot;&gt;2&lt;/span&gt;&gt;&lt;/span&gt;/dev/null&lt;span class=&quot;token variable&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-n&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;$git_root&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;then&lt;/span&gt;&lt;br&gt;    &lt;span class=&quot;token builtin class-name&quot;&gt;unset&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;_git_identity_warned_repos[&lt;span class=&quot;token variable&quot;&gt;$git_root&lt;/span&gt;]&quot;&lt;/span&gt;&lt;br&gt;  &lt;span class=&quot;token keyword&quot;&gt;fi&lt;/span&gt;&lt;br&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using agents to create small tools to make my life easier as a developer is something that I enjoy. I like that I can explain my problem, brainstorm a solution with an agent and have a tool that I wouldn&#39;t have created otherwise.&lt;/p&gt;
</content>
    </entry>
  
</feed>