<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Lars Richter</title>
    <description>The latest articles on Forem by Lars Richter (@n_develop).</description>
    <link>https://forem.com/n_develop</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4405%2F6517893f-a359-4e92-9906-482a70af6976.jpeg</url>
      <title>Forem: Lars Richter</title>
      <link>https://forem.com/n_develop</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/n_develop"/>
    <language>en</language>
    <item>
      <title>Do You Do Home Automation? Join Us!</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Thu, 22 Apr 2021 11:06:00 +0000</pubDate>
      <link>https://forem.com/n_develop/do-you-do-home-automation-join-us-1fkf</link>
      <guid>https://forem.com/n_develop/do-you-do-home-automation-join-us-1fkf</guid>
      <description>&lt;p&gt;A lot of developers like tinkering with all sorts of tech. And if you own, build or rent a house or an apartment, home automation is a cool area to discover and play with.&lt;/p&gt;

&lt;p&gt;But home automation can be frustrating as well. Which system should I choose? Should I go with an open-source system like Home Assistant? Or maybe with something like Philips Hue? Where do I start? Lighting? Heating? Cameras? Door locks?&lt;/p&gt;

&lt;p&gt;But even if you found a system to go with, there will be hurdles. I mean, it's tech, isn't it? 😉 And that's where a community would be awesome. Asking questions. Giving advice to others. That's what we are here for. And &lt;a href="https://dev.to/ben"&gt;Ben&lt;/a&gt;  created a special (of course Forem-powered) community for everything related to technology in your home.&lt;/p&gt;

&lt;h2&gt;
  
  
  Join &lt;a href="https://www.hometechnica.com"&gt;Hometechnica&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It's not just about home automation. There is much more. Here is the "tagline" of Hometechnica.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Homebuilding in the age of automation, privacy, security, and alternative energy. Gadgets, ideas, and community.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you are interested in any of these topics, come join us over there. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.hometechnica.com"&gt;https://www.hometechnica.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will be fun. I promise. 😄&lt;/p&gt;

</description>
      <category>homeautomation</category>
      <category>smarthome</category>
      <category>community</category>
      <category>hometechnica</category>
    </item>
    <item>
      <title>When Over-Engineering Hits You Hard</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Wed, 15 Jul 2020 08:34:10 +0000</pubDate>
      <link>https://forem.com/n_develop/over-engineering-is-real-47pl</link>
      <guid>https://forem.com/n_develop/over-engineering-is-real-47pl</guid>
      <description>&lt;p&gt;I planned to write this post for a couple of weeks now. But recently I saw this discussion post from Ben:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/ben" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1%2Ff451a206-11c8-4e3d-8936-143d0a7e65bb.png" alt="ben"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/ben/how-do-you-identify-over-engineering-1oad" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How do you identify "over-engineering"?&lt;/h2&gt;
      &lt;h3&gt;Ben Halpern ・ Jul 14 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#discuss&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#codequality&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;This reminded me, that I should get back to this post and finish it.&lt;/p&gt;

&lt;h2&gt;
  
  
  It sounded so simple
&lt;/h2&gt;

&lt;p&gt;We use custom NuGet packages and an internal NuGet-server at work to make reusable code easily available. One of those NuGet packages is a "utilities" package, that contains functions and services that come in handy quite often.&lt;/p&gt;

&lt;p&gt;A few weeks ago, I wanted to use the &lt;code&gt;EnumUtils&lt;/code&gt;. As you can probably guess by now, it provides functions to work with &lt;code&gt;Enums&lt;/code&gt;. For example, reading a string representation for an enum-member that is defined in a &lt;code&gt;Description&lt;/code&gt; attribute. But let's look at an example. Let's assume we have an enum called &lt;code&gt;TicketStatus&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;TicketStatus&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"open"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"in progress"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;InProgress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"testing"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Testing&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"approval"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Approval&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"done"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;Done&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And somewhere in my code, I want to get the description for a specific value of the enum.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;TicketStatus&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetCurrentTicketStatus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// now what?&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, now what? I knew that class was called &lt;code&gt;EnumUtils&lt;/code&gt;. But &lt;code&gt;EnumUtils.GetDescription(status)&lt;/code&gt; did not work. There is no &lt;code&gt;EnumUtils&lt;/code&gt; class. But my IDE told me, that there is something called &lt;code&gt;IEnumUtils&lt;/code&gt;. OK... I get it. We use dependency injection a lot. Let's get an &lt;code&gt;IEnumUtils&lt;/code&gt; thing injected into the constructor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SomeServiceOrController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="n"&gt;IEnumUtils&lt;/span&gt; &lt;span class="n"&gt;_enumUtils&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;SomeServiceOrController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;IEnumUtils&lt;/span&gt; &lt;span class="n"&gt;enumUtils&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_enumUtils&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;enumUtils&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;DoSomething&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;TicketStatus&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;GetCurrentTicketStatus&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;$"Some message including '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;_enumUtils&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s"&gt;'"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cm"&gt;/* more methods here ... */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That was easy. And let me tell you something. It worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Then, what's your problem? 🧐
&lt;/h2&gt;

&lt;p&gt;Good question. 😄&lt;/p&gt;

&lt;p&gt;My problems with this came up when I started writing the tests for my &lt;code&gt;DoSomething()&lt;/code&gt; method. Here is what I did:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;DoSomething_IsWorkingFine&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cm"&gt;/* Doing stuff to create the test scenario... */&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sut&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SomeServiceOrController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* what to do here? */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sut&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;DoSomething&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Some message including 'in progress'"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What do I pass to the constructor of &lt;code&gt;SomeServiceOrController&lt;/code&gt;? I already found out earlier that I cannot do &lt;code&gt;new EnumUtils()&lt;/code&gt; because "EnumUtils" is not found. Sure, I could write a small hand-written mock or just use a mocking framework. This would totally work. But to be honest... &lt;strong&gt;I don't want to do that&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I mean... it's just a simple utils function. Why would I mock that? It just reads an attribute from an enum member. I see no need to mock that. That's why I checked the code of the utils package. And I found the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnumUtils&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEnumUtils&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nf"&gt;GetDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Enum&lt;/span&gt; &lt;span class="k"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="cm"&gt;/* the implementation is not important here */&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do you see it? It's &lt;code&gt;internal&lt;/code&gt;. That's why I cannot use &lt;code&gt;new EnumUtils()&lt;/code&gt; in my project.&lt;br&gt;
And I thought "But what if I don't have dependency injection available? There might be smaller projects, where we do not have DI in place. How would I use it there?". So I looked a little deeper in the code...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;IEnumUtilFactory&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IEnumUtils&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnumUtilFactory&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;IEnumUtilFactory&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;IEnumUtils&lt;/span&gt; &lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EnumUtils&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, the good part: Now I can use the factory in my test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sut&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SomeServiceOrController&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;EnumUtilFactory&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Create&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I don't have to mock that stuff. Great. &lt;strong&gt;But...&lt;/strong&gt;&lt;br&gt;
An additional interface (IEnumUtilsFactory) and an additional class (EnumUtilsFactory) just to wrap the &lt;code&gt;new EnumUtils()&lt;/code&gt;? Really? Why does this thing need a factory? I don't get that. &lt;/p&gt;

&lt;h2&gt;
  
  
  Time to calm down
&lt;/h2&gt;

&lt;p&gt;I felt the urge to change the utils package immediately. This implementation is just wrong. I will fix it and the world will be a better place.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/xUA7b7pLM4w1edn0yI/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/xUA7b7pLM4w1edn0yI/giphy.gif" alt="calm down gif"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After taking a short break and thinking about it a little more, my initial shock was gone. But another shock came. I realized that I did something I can't stand when others do it. I mean the "This is awful code. Why did anyone create it that way? I need to rewrite it to make it good." attitude.&lt;/p&gt;

&lt;p&gt;You should always keep in mind, that the developer who created this code most likely had good reasons to do it that way. Maybe there were specific requirements. Maybe there were other constraints. Or they were just learning these concepts and thought it would be a good use-case for it. I mean, we have all been there, right?&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Over-engineering will always happen. Over-engineering is real. Sometimes it is just a few classes that could be reduced to a simple function. Sometimes it is worse and the entire system architecture is excessively over-engineered. But again: There will be over-engineering and we have to deal with it from time to time. &lt;/p&gt;

&lt;p&gt;What about my scenario? I will talk to my colleagues about how we will deal with it. Maybe making the &lt;code&gt;EnumUtils&lt;/code&gt; public would be a start. Or maybe keep the code as it is for now and create an extension method that does the same and can be used in new projects.&lt;/p&gt;

&lt;p&gt;I think my takeaway is: Don't focus on the "bad code" and how you would have done it better. Focus on how to solve the problem at hand.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>bestpractices</category>
      <category>csharp</category>
      <category>rant</category>
    </item>
    <item>
      <title>TCR - test &amp;&amp; commit || revert</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Tue, 14 Jul 2020 19:17:13 +0000</pubDate>
      <link>https://forem.com/n_develop/tcr-test-commit-revert-f9m</link>
      <guid>https://forem.com/n_develop/tcr-test-commit-revert-f9m</guid>
      <description>&lt;p&gt;I love to try new programming tools and techniques. Especially if they involve TDD, unit testing, or something related. A few months ago I learned about "test &amp;amp;&amp;amp; commit || revert" and it instantly looked like fun to me.&lt;/p&gt;

&lt;p&gt;"test &amp;amp;&amp;amp; commit || revert" (or just "TCR") was originally invented during a Code Camp in Oslo by Lars Barlindhaug, Oddmund Strømme, Ole Johannessen and Kent Beck.&lt;br&gt;
Kent Beck wrote about this &lt;a href="https://medium.com/@kentbeck_7670/test-commit-revert-870bbd756864"&gt;in this blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The main idea
&lt;/h2&gt;

&lt;p&gt;The main idea comes from &lt;a href="https://medium.com/@kentbeck_7670/limbo-scaling-software-collaboration-afd4f00db4b"&gt;Kent Beck's "Limbo Story"&lt;/a&gt;. It's all about making changes as small as possible so that they can be easily shared &lt;strong&gt;and merged&lt;/strong&gt;. But how do you enforce small changes?&lt;/p&gt;

&lt;p&gt;TCR works best if you have some kind of file watcher or any other trigger that will start the TCR process immediately "on save". &lt;br&gt;
So every time you make a change (and save your files), the test will be run. If all your tests pass, the code will be committed to source control. So every state of your code that is successfully validated by the tests is committed. But here comes the interesting part: If the tests don't pass, your changes will be reverted. Sound scary, doesn't it?&lt;/p&gt;

&lt;p&gt;In fact, it is not that scary if you think about it. Every little change you make will be committed separately. So you will just lose a small amount of work.&lt;/p&gt;
&lt;h2&gt;
  
  
  Show me the code
&lt;/h2&gt;

&lt;p&gt;Well "code" is a big word here. But let's look at the command that is used. I am a .NET developer, so I use &lt;code&gt;dotnet test&lt;/code&gt; to run my tests. But you can replace that part with whatever works for your stack.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-am&lt;/span&gt; working &lt;span class="o"&gt;||&lt;/span&gt; git reset &lt;span class="nt"&gt;--hard&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's really that easy.&lt;/p&gt;

&lt;h2&gt;
  
  
  "But my git log..." 😨
&lt;/h2&gt;

&lt;p&gt;Sometimes, when I talk to other developers about TCR, their reaction is something like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/fpXxIjftmkk9y/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/fpXxIjftmkk9y/giphy.gif" alt="OMG!"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Are you serious? Have you thought about the git log? It will just say "working" all the time. That's not helpful at all.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's true. I don't think using "working" as a message for every commit is not super helpful. But I think it is not a big deal as well. When you are done with a specific part of your feature/bugfix, just squash your commits before you push your changes. Problem solved.&lt;/p&gt;

&lt;h2&gt;
  
  
  My experience with TCR
&lt;/h2&gt;

&lt;p&gt;I have tried TCR using some &lt;a href="https://codingdojo.org/kata/"&gt;katas&lt;/a&gt; and also on a few small side projects. I think it is fun. It forces you to make small steps which is a good practice in general.&lt;/p&gt;

&lt;p&gt;But it comes with some extra costs as well. As mentioned in the last paragraph, you don't want to push your "working" commits, do you? That means you have to squash your commits manually all the time. Of course, it is not a big deal, but it's an extra step.&lt;/p&gt;

&lt;p&gt;Also, I'm a big fan of TDD. And to be honest, I really miss the "watch it fail" phase when doing TCR. If you run the TCR process on every "save" event, you cannot watch your new test fail, because that will revert your last change (which was the creation of the test). &lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;I don't think you have to use TCR all the time and everywhere. But it is fun to try it. And you will learn to take super small steps. Again, you don't have to make these tiny-tiny steps all the time, but I think you should have the ability to do so. It might come in handy someday.&lt;/p&gt;

&lt;h2&gt;
  
  
  More resources on TCR
&lt;/h2&gt;

&lt;p&gt;There are a lot of cool resources on TCR.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you like podcasts, you should check out the &lt;a href="https://hanselminutes.com/663/test-commit-revert-with-kent-beck"&gt;"TCR" episode of "Hanselminutes" by Scott Hanselman&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The SoCraTes UK community has a couple of &lt;a href="https://www.youtube.com/user/dkandalov/search?query=tcr"&gt;TCR session available on YouTube&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The awesome Kent Beck has &lt;a href="https://www.youtube.com/watch?v=Aof0F9DvTFg&amp;amp;list=PLlmVY7qtgT_nhLyIbeAaUlFOWbWT5y53t"&gt;a cool TCR introduction&lt;/a&gt; on YouTube as well&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>testing</category>
      <category>tdd</category>
      <category>unittests</category>
      <category>unittesting</category>
    </item>
    <item>
      <title>I'm Giving Away Coding-Related Books</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Sat, 04 Jul 2020 11:35:13 +0000</pubDate>
      <link>https://forem.com/n_develop/i-m-giving-away-coding-related-books-3flk</link>
      <guid>https://forem.com/n_develop/i-m-giving-away-coding-related-books-3flk</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;I'm giving a few coding related books. Most of them are in German (sorry for that), but two are in English. You just have to pay for the shipping. I hope those books can help anyone learn stuff, even if they are a little dated.&lt;/p&gt;

&lt;p&gt;Just check out my twitter thread. It contains all the books.&lt;/p&gt;


&lt;blockquote class="ltag__twitter-tweet"&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--NXE-kL9y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1279376644382363649/ewFM1-UD_normal.jpg" alt="Lars Richter profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Lars Richter
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/n_develop"&gt;@n_develop&lt;/a&gt;
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Hey Twitter,&lt;br&gt;I'm giving some old books away for free. You only have to pay for the shipping. Just two of the books are in English. The rest are german. I would be happy if those books can help anyone learn stuff, even if they are a little dated. Every RT is very welcome.
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      10:58 AM - 04 Jul 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1279368992969326592" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1279368992969326592" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1279368992969326592" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;blockquote class="ltag__twitter-tweet"&gt;
    &lt;div class="ltag__twitter-tweet__media ltag__twitter-tweet__media__two-pics"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2EZ4RhqL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/EcE-XtZWkAESEOs.jpg" alt="unknown tweet media content"&gt;
    &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--NXE-kL9y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1279376644382363649/ewFM1-UD_normal.jpg" alt="Lars Richter profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Lars Richter
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/n_develop"&gt;@n_develop&lt;/a&gt;
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      Of course I forgot, the photos... 🙈&lt;br&gt;So here they are. 
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      11:12 AM - 04 Jul 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1279372429899575297" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1279372429899575297" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1279372429899575297" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;
&lt;br&gt;
&lt;blockquote class="ltag__twitter-tweet"&gt;
    &lt;div class="ltag__twitter-tweet__media ltag__twitter-tweet__media__two-pics"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Fe5-cGAo--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/media/EcE-jNTX0AEnloP.jpg" alt="unknown tweet media content"&gt;
    &lt;/div&gt;

  &lt;div class="ltag__twitter-tweet__main"&gt;
    &lt;div class="ltag__twitter-tweet__header"&gt;
      &lt;img class="ltag__twitter-tweet__profile-image" src="https://res.cloudinary.com/practicaldev/image/fetch/s--NXE-kL9y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://pbs.twimg.com/profile_images/1279376644382363649/ewFM1-UD_normal.jpg" alt="Lars Richter profile image"&gt;
      &lt;div class="ltag__twitter-tweet__full-name"&gt;
        Lars Richter
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__username"&gt;
        &lt;a class="mentioned-user" href="https://dev.to/n_develop"&gt;@n_develop&lt;/a&gt;
      &lt;/div&gt;
      &lt;div class="ltag__twitter-tweet__twitter-logo"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ir1kO05j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-f95605061196010f91e64806688390eb1a4dbc9e913682e043eb8b1e06ca484f.svg" alt="twitter logo"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__body"&gt;
      
    &lt;/div&gt;
    &lt;div class="ltag__twitter-tweet__date"&gt;
      11:12 AM - 04 Jul 2020
    &lt;/div&gt;


    &lt;div class="ltag__twitter-tweet__actions"&gt;
      &lt;a href="https://twitter.com/intent/tweet?in_reply_to=1279372583805296640" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fFnoeFxk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-reply-action-238fe0a37991706a6880ed13941c3efd6b371e4aefe288fe8e0db85250708bc4.svg" alt="Twitter reply action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/retweet?tweet_id=1279372583805296640" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--k6dcrOn8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-retweet-action-632c83532a4e7de573c5c08dbb090ee18b348b13e2793175fea914827bc42046.svg" alt="Twitter retweet action"&gt;
      &lt;/a&gt;
      &lt;a href="https://twitter.com/intent/like?tweet_id=1279372583805296640" class="ltag__twitter-tweet__actions__button"&gt;
        &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SRQc9lOp--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/twitter-like-action-1ea89f4b87c7d37465b0eb78d51fcb7fe6c03a089805d7ea014ba71365be5171.svg" alt="Twitter like action"&gt;
      &lt;/a&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/blockquote&gt;


</description>
      <category>books</category>
      <category>giveaway</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How Do You Use Your StreamDeck?</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Sat, 04 Apr 2020 13:25:19 +0000</pubDate>
      <link>https://forem.com/n_develop/how-do-you-use-your-streamdeck-1g94</link>
      <guid>https://forem.com/n_develop/how-do-you-use-your-streamdeck-1g94</guid>
      <description>&lt;p&gt;I bought a used &lt;a href="https://www.elgato.com/de/gaming/stream-deck-mini"&gt;StreamDeck Mini&lt;/a&gt; yesterday and I'm really excited about it. I hope it will be delivered by Monday. I'm not a streamer (yet), but I think it will make an awesome productivity gadget.&lt;/p&gt;

&lt;p&gt;I have some things in mind, that I will try:&lt;/p&gt;

&lt;h3&gt;
  
  
  In my IDE
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Run all tests&lt;/li&gt;
&lt;li&gt;Run unit tests only&lt;/li&gt;
&lt;li&gt;Run integration tests only&lt;/li&gt;
&lt;li&gt;Run tests in the current test session&lt;/li&gt;
&lt;li&gt;Run code coverage test&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  In Microsoft Teams
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Toggle mic&lt;/li&gt;
&lt;li&gt;Toggle camera&lt;/li&gt;
&lt;li&gt;Toggle "blur background"&lt;/li&gt;
&lt;li&gt;Accept call&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  General work stuff
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Start specific Jenkins pipelines&lt;/li&gt;
&lt;li&gt;Start my IDE (JetBrains Rider)&lt;/li&gt;
&lt;li&gt;Start VS Code&lt;/li&gt;
&lt;li&gt;Start WSL (Windows Subsystem for Linux)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm totally aware, that most of the actions can be performed by a keyboard shortcut. But it's still fun to have such a cool gadget at my fingertips.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update 21.06.2020
&lt;/h3&gt;

&lt;p&gt;I have been using my StreamDeck mini almost every (work)day since I got it. And the way I use it changed a bit from what I have planned. So here is a short update on how I (really) use my StreamDeck.&lt;/p&gt;

&lt;p&gt;You can have different configurations (called "profiles") for different applications. For example, I do have a profile for my IDE (Jetbrains Rider). In this profile a have buttons for "Run all my tests", "Cover all my tests" and so on. But I realized, that I still use the normal keyboard shortcuts for that. So I don't really use it there.&lt;/p&gt;

&lt;p&gt;But I do use it &lt;strong&gt;a lot&lt;/strong&gt; in Microsoft Teams. I do have "Toggle Mic" and "Toggle Webcam" to use in the video calls. And I have buttons to set my status to "Be right back", "Away" or back to "Available".&lt;/p&gt;

&lt;p&gt;And there is one more thing. It's &lt;a href="https://obsproject.com/"&gt;OBS Studio&lt;/a&gt;. That's a fantastic piece of software. Maybe you have heard of it. It's mostly used by streamers and people recording videos. But I don't really do both. I use it for video conferences.&lt;/p&gt;

&lt;p&gt;You can create "scenes" in OBS which you can select by a single push of a button (on your StreamDeck). You can assemble these scenes just as you like. My "regular" scene is just my webcam signal. Nothing fancy here. The next thing is my "Be right back" scene. This shows a gif of a coffee mug with a "BRB" post-it on it. Then I have a "this is awesome" scene, where a put a confetti gif (with a transparent background) right over my webcam signal. It's always fun to use that one. :-) &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Hrkb8KfL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kxoomatl4kj64pt0doxi.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Hrkb8KfL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kxoomatl4kj64pt0doxi.gif" alt="My this is awesome scene"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And then there is my... let's call it... "boring meeting" scene... or maybe "I'll take a nap" scene. This will show a prerecorded 60-second video of me in a never-ending loop. And to be fair: I don't use it in boring meetings. I use it if the other people in the call do not have their webcam on. I think it's a matter of courtesy to activate your webcam during a video conference. So if I can't see them, they don't get the "real me". :-)&lt;/p&gt;

&lt;p&gt;And getting back to my StreamDeck: All these scenes are always at my fingertips with my StreamDeck. It's a ton of fun.&lt;/p&gt;

&lt;h3&gt;
  
  
  It's your turn.
&lt;/h3&gt;

&lt;p&gt;Do you have a StreamDeck? If so, how do you use it? Are you a streamer? Do you use it for productivity? Gaming? Do you have cool profiles or tips to share? Let me know in the comments.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>productivity</category>
      <category>streamdeck</category>
    </item>
    <item>
      <title>Would You Be Interested In A Conference Talk About The Different "Schools Of TDD"?</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Fri, 28 Feb 2020 06:17:53 +0000</pubDate>
      <link>https://forem.com/n_develop/would-you-be-interested-in-a-conference-talk-about-the-different-schools-of-tdd-eg0</link>
      <guid>https://forem.com/n_develop/would-you-be-interested-in-a-conference-talk-about-the-different-schools-of-tdd-eg0</guid>
      <description>&lt;p&gt;I'm planning to apply at two or three conferences this year. While thinking about possible topics, I thought about one of my favorite topics: Test-Driven Development. I like to read and talk about TDD. It sparks interesting discussions, like the &lt;a href="https://martinfowler.com/articles/is-tdd-dead/"&gt;"Is TDD dead" discussion&lt;/a&gt;, and forces developers to think about their processes and workflows.&lt;/p&gt;

&lt;p&gt;While I'm familiar with the "classic" schools of TDD, the "Detroit School" and the "London School", I recently learned, that there is more. &lt;a href="https://twitter.com/davidvoelkel"&gt;David Völkel&lt;/a&gt; invented the "Munich School of TDD". And, staying in Germany for that matter, there is also a "St. Pauli School of TDD". These instantly sparked my interest. So at the moment, I'm trying to play with these new concepts.&lt;/p&gt;

&lt;p&gt;Do you think a talk about the different schools of TDD would be worth preparing? Or would it be "just another TDD talk, nobody wants to hear"? I would love to hear your opinion about that.&lt;/p&gt;

</description>
      <category>tdd</category>
      <category>testing</category>
      <category>discuss</category>
      <category>publicspeaking</category>
    </item>
    <item>
      <title>Organizing my life using Microsoft ToDo</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Thu, 13 Feb 2020 04:43:22 +0000</pubDate>
      <link>https://forem.com/n_develop/organizing-my-life-using-a-todo-app-16ll</link>
      <guid>https://forem.com/n_develop/organizing-my-life-using-a-todo-app-16ll</guid>
      <description>&lt;p&gt;I forget a lot of stuff. I really do. When I was younger, my mother always said: "If your head wouldn't be attached to your body, you wouldn't take it with you". (Sounds a little less strange in German, but you get the drift)&lt;/p&gt;

&lt;p&gt;Over the years I tried of lot of things to organize my life. For the last couple of years, I used a shared calendar with my wife. That worked good. Not perfect, but good.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Microsoft To Do" to the rescue
&lt;/h2&gt;

&lt;p&gt;By the end of last year, I started using &lt;a href="https://todo.microsoft.com/" rel="noopener noreferrer"&gt;Microsoft To Do&lt;/a&gt; more and more. For me, it started with being a simple checklist for "go shopping" and "buy a birthday present for Mark". But it changed a lot in the last weeks and months.&lt;/p&gt;

&lt;p&gt;At the moment I have 15 lists in the app (not including the smart lists provided by the app). I have individual lists for stuff I have to do at home (like cleaning), blogging, tasks for my website, conference talks I want to create, CFP (Call for Papers) deadline, movies I want to watch, etc. etc.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkgxdl1ze7cgb8a6kdc1k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkgxdl1ze7cgb8a6kdc1k.png" alt="Microsoft To Do screenshots"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Planing even "silly" recurring tasks
&lt;/h2&gt;

&lt;p&gt;Some days I get up in the morning, look in the mirror and think "Boy, you really need a haircut". Or maybe "Man, I should have shaved days ago. I look like a caveman".&lt;/p&gt;

&lt;p&gt;That's why I even schedule these simple tasks using this app. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Haircut" - every 4 weeks&lt;/li&gt;
&lt;li&gt;"Shave" - every second day&lt;/li&gt;
&lt;li&gt;"Vacuum the house" - twice a week&lt;/li&gt;
&lt;li&gt;...&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sounds stupid to you? Maybe. But it works well for me. I haven't had these "caveman moments" for quite some time. 😆&lt;/p&gt;

&lt;h2&gt;
  
  
  What should I do today?
&lt;/h2&gt;

&lt;p&gt;Microsoft To Do comes with a pretty awesome feature called "My Day". It's a list you can fill with tasks from other lists. It also has recommendations (based on due dates and more) for tasks that you could add to "My Day". And every day you will start with an empty "My Day" list. All the tasks you did not finish yesterday will be removed.&lt;/p&gt;

&lt;p&gt;You also have an "Add" button in "My Day". That doesn't make any sense, right? If the list is cleared every morning, where are the items, that I added to this list yesterday? The answer is "In your &lt;code&gt;Tasks&lt;/code&gt; list". Every item you add to "My Day" will be created in "Tasks" and added to "My Day" as well. So no item will be lost.&lt;/p&gt;

&lt;p&gt;Every morning I start my day by filling the "My Day" list. The great thing about that: During the day I only have to &lt;strong&gt;check a single list&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcq4fiv8nip0bug7p3cue.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fcq4fiv8nip0bug7p3cue.gif" alt="My Day animation"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Staying social
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Too bad, that this former co-worker/old friend/someone I liked and I lost touch.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Does this sound familiar? That's a thought I had a lot of times in my life. That's why I have a list called "social life". In this list I have scheduled tasks to stay in contact with people I don't meet regularly. I scheduled those tasks for about "every 4 months" or "every 4 weeks", depending on the person.&lt;/p&gt;

&lt;h2&gt;
  
  
  Happy Birthday, Anna!
&lt;/h2&gt;

&lt;p&gt;I took this list stuff even further and added all the important birthdays to a separate list. I assigned a reminder and a due date for each birthday. "But how is this different than an event in your calendar?" you might think. Well, in the morning I will add the "Say Happy Birthday to Anna" task to "My Day". Every time I check "My Day" I will be reminded to send a message to Anna. &lt;/p&gt;

&lt;p&gt;But the biggest advantage for me: It still might happen (for whatever reason), that I forget to send this message. But the next morning, when planing out "My Day", I will see a task "Say Happy Birthday to Anna" in red letters (because the due date passed) at the top of the recommendations. The message might be a day late, but I will write that message. 👍&lt;/p&gt;

&lt;h2&gt;
  
  
  Organizing the lists
&lt;/h2&gt;

&lt;p&gt;When you start creating more lists, you might feel the need for more structure. Luckily, you can group lists. So I have a "Work" group, a group for my "Collections" (like "Movies to watch" and "Birthdays"), a group for all my technical (but private) stuff (like "Blogging ideas" or the tasks for my personal website). This way, the list overview stays organized and clean. &lt;/p&gt;

&lt;h2&gt;
  
  
  Sharing is caring
&lt;/h2&gt;

&lt;p&gt;And of course, like any other good ToDo app, you can share lists with others. If you are planning a trip with friends or have a permanent ToDo list wife your spouse, just share a list and you are good to go.&lt;/p&gt;

&lt;p&gt;Now, tell me about your tips and tricks. How do you organize your life? Do you use a specific app? Or maybe you are like my wife and can remember pretty much everything? Let me know in the comments.&lt;/p&gt;




&lt;p&gt;All images were kindly provided by Microsoft and taken from &lt;a href="https://www.microsoft.com/en-us/microsoft-365/blog/2019/09/09/announcing-new-version-microsoft-to-do/" rel="noopener noreferrer"&gt;https://www.microsoft.com/en-us/microsoft-365/blog/2019/09/09/announcing-new-version-microsoft-to-do/&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>tools</category>
      <category>todo</category>
    </item>
    <item>
      <title>Handle flaky tests with quarantine and Xunit.SkippableFact</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Fri, 07 Feb 2020 12:32:15 +0000</pubDate>
      <link>https://forem.com/n_develop/handle-flaky-tests-with-quarantine-and-xunit-skippablefact-3a14</link>
      <guid>https://forem.com/n_develop/handle-flaky-tests-with-quarantine-and-xunit-skippablefact-3a14</guid>
      <description>&lt;p&gt;We maintain the integration of a lot of third party services in my team. Five payment service provider, a tax service, ERP systems, etc. etc.&lt;/p&gt;

&lt;p&gt;Besides or unit tests we also have a couple of integration tests (or &lt;a href="https://martinfowler.com/bliki/ContractTest.html"&gt;integration contract tests&lt;/a&gt;) for every third-party service, to make sure that our client libraries still match their API. Sadly, the test environments of the third-party systems aren't always reliable. For example, one of the payment service providers fails during one out of 30 requests (some times even more often). For this provider, we have 8 integration tests (one for each function we are using) that will run during our CI pipeline.&lt;/p&gt;

&lt;p&gt;This means, that every third or fourth run of our CI pipeline will fail due to this error. Reaching out to the payment service provider had no effect so far. So these 8 tests are classical "flaky tests". There are a lot of good posts here on dev.to about flaky tests and why they are really bad. Two awesome examples are&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/kickingthetv" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---emjE7u3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--bkGQ248t--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/275279/cf8d9d54-7ab4-4d32-94f4-62867d8d3a78.JPG" alt="kickingthetv"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/kickingthetv/we-have-a-flaky-test-problem-11ol" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;We Have A Flaky Test Problem&lt;/h2&gt;
      &lt;h3&gt;Bryan Lee ・ Dec 9 '19 ・ 18 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#flakytests&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#microservices&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#distributedsystems&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;



&lt;div class="ltag__link"&gt;
  &lt;a href="/mlapierre" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KqpaiH2B--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--g8YAExnL--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_66%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/18369/ba302c5b-ccae-4ff7-b0b1-692e24a75b69.gif" alt="mlapierre"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/mlapierre/pros-and-cons-of-quarantined-tests-2emj" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Pros and Cons of Quarantined Tests&lt;/h2&gt;
      &lt;h3&gt;Mark Lapierre ・ Jun 6 '18 ・ 3 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#automation&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#qa&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;A lot of posts about this topic will explore ways to avoid flaky tests and that is a good idea. But those "contract tests" (depending on your third party system) might always be flaky. So how to deal with those tests? We cannot delete them, because they still provide value.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quarantine flaky tests?
&lt;/h2&gt;

&lt;p&gt;When I started to read up on flaky tests, I found that some people proposed to put all flaky tests you cannot delete in a separate test suite. For more information about that, read the before mentioned post about quarantined tests, or maybe the post by Martin Fowler called &lt;a href="https://martinfowler.com/articles/nonDeterminism.html"&gt;Eradicating Non-Determinism in Tests&lt;/a&gt;.  The idea is to move the flaky (or in this case called quarantined) tests out of your CI pipeline to keep it healthy.&lt;/p&gt;

&lt;p&gt;I totally understand the point, but deep inside I still wanted to run all the tests during our CI pipeline, because I wanted to see problems as soon as possible. But how do I keep those tests in the pipeline without having the pipeline broken every third or fourth run for "no reason"?&lt;/p&gt;

&lt;h2&gt;
  
  
  Skip tests for a specific reason?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Wouldn't it be great if the tests would still fail if they have a "real problem", but just ignore the test result if they fail for the connection issue?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's what I thought. So I tried to find a way to skip the tests if we ran into the known connection issue with the payment service provider. But in &lt;a href="https://xunit.net/"&gt;xUnit.net&lt;/a&gt; there is no way to say &lt;code&gt;Assert.Skip();&lt;/code&gt; or &lt;code&gt;Assert.Inconclusive();&lt;/code&gt; or something like that.&lt;/p&gt;

&lt;p&gt;But then I found this library called &lt;a href="https://github.com/AArnott/Xunit.SkippableFact"&gt;Xunit.SkippableFact&lt;/a&gt;. It does exactly what I need. With it, I can mark my tests (called &lt;code&gt;Fact&lt;/code&gt; in xUnit.net) as &lt;code&gt;SkippableFact&lt;/code&gt; and provide a way to tell it, which result to skip/ignore.&lt;/p&gt;

&lt;p&gt;Let's look at a small example. Image a super useful service, that will receive a string and return the length of this string. And of course, the connection to this service is pretty unstable. About every third connection will run into a &lt;code&gt;SecurityConncection&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UnstableServiceConnection&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;GetLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Millisecond&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;%&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SecurityException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Something is insecure here!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The tests might look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UnstableConnectionTests&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;Fact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GivenShortText_ReturnsCorrectLength&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UnstableServiceConnection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ABCD"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On average, every third run of our nice test will fail. Sad, isn't it.&lt;/p&gt;

&lt;p&gt;But with Xunit.SkippableFact we can do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UnstableConnectionTests&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;SkippableFact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;SecurityException&lt;/span&gt;&lt;span class="p"&gt;))]&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;GivenShortText_ReturnsCorrectLength&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;UnstableServiceConnection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetLength&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ABCD"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Should&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;Be&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the test will be skipped, if it throws a &lt;code&gt;SecurityException&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And you are not limited to exceptions. You could check for almost anything inside of your test. Just use &lt;code&gt;Skip.IfNot(myBooleanValueOrExpression);&lt;/code&gt; inside of your test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;SkippableFact&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;SomeTestForWindowsOnly&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Skip&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;IfNot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IsWindows&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Test Windows only functionality.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Example copied from &lt;a href="https://github.com/AArnott/Xunit.SkippableFact"&gt;https://github.com/AArnott/Xunit.SkippableFact&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The two-step process
&lt;/h2&gt;

&lt;p&gt;In the end, we did both. We copied the flaky tests to a separate test suite that has to run "green" before a deployment. But we also kept the same tests as "SkippableFacts" in the CI pipeline. I think for us this was the best way to address these problems.&lt;/p&gt;

&lt;p&gt;Now it's your turn. What do you think about it? Do you have similar problems? If so, how do you handle it?&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>testing</category>
      <category>ci</category>
    </item>
    <item>
      <title>Speeding Up Our Unit Tests From 15min To Less Than 2min</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Sat, 25 Jan 2020 20:30:55 +0000</pubDate>
      <link>https://forem.com/n_develop/speeding-up-our-unit-tests-from-15min-to-less-than-2min-1c5f</link>
      <guid>https://forem.com/n_develop/speeding-up-our-unit-tests-from-15min-to-less-than-2min-1c5f</guid>
      <description>&lt;p&gt;Our codebase at work is pretty well tested. The code coverage is at 80+ percent. I'm working on our backoffice systems, which are developed on the .NET platform and consist of about 100 projects (services and components) and growing. &lt;br&gt;
Most of our services/components live in a separate solution in which we also store the test projects for unit and integration tests. &lt;br&gt;
While writing more code (and therefore more tests), the time needed for running our tests increased. By the end of last year (2019) the unit tests took 15 minutes to run.&lt;/p&gt;
&lt;h2&gt;
  
  
  Time for a Hackday!
&lt;/h2&gt;

&lt;p&gt;But everything worked. In my previous companies, it would be difficult to get permission to "play" with the build and test scripts to improve it. But at my current company,&lt;br&gt;
we do have the concept of "hackdays". Every month you can use one day to do stuff outside of the normal schedule. Maybe there is some code you always wanted to refactor but you never had the time to do so. Or maybe you wanted to extend the UI tests. Or you want to take a look into some new framework or technology that might be relevant for your work.&lt;br&gt;
Whatever it is, you can work on those things one day every month.&lt;/p&gt;

&lt;p&gt;So I invested a "hackday" to improve the speed of our test suite. I found out, that the tests itself weren't terribly slow. It seemed like the tests were not run in parallel.&lt;/p&gt;
&lt;h2&gt;
  
  
  Our old way
&lt;/h2&gt;

&lt;p&gt;Why wasn't it running in parallel? To understand that, you have to understand the build and test process.&lt;br&gt;
We had a &lt;code&gt;csproj&lt;/code&gt; file to build and run our entire codebase. It uses MSBuild targets and commands to gather all relevant projects.&lt;br&gt;
A simplified version of it would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;  &lt;span class="c"&gt;&amp;lt;!-- ... doing the build somewhere here first ... --&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;UnitTestProjects&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"**\*.Test.XUnit.csproj"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;ItemGroup&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;IntegrationTestProjects&lt;/span&gt; &lt;span class="na"&gt;Include=&lt;/span&gt;&lt;span class="s"&gt;"**\*.Test.XIntegration.csproj"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ItemGroup&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;Target&lt;/span&gt; &lt;span class="na"&gt;Name=&lt;/span&gt;&lt;span class="s"&gt;"unittest"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class="na"&gt;Command=&lt;/span&gt;&lt;span class="s"&gt;"dotnet.exe test %(UnitTestProjects.Identity) --no-build --no-restore"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;Target&lt;/span&gt; &lt;span class="na"&gt;Name=&lt;/span&gt;&lt;span class="s"&gt;"integrationtest"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;Exec&lt;/span&gt; &lt;span class="na"&gt;Command=&lt;/span&gt;&lt;span class="s"&gt;"dotnet.exe test %(IntegrationTestProjects.Identity) --no-build --no-restore"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/Target&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I run MSBuild on this project file with the target "unittest", it will find all relevant projects and run them using &lt;code&gt;dotnet test&lt;/code&gt;. Finding the projects is pretty simple because we have a naming convention, which says that the unit test projects must be named like this: &lt;code&gt;[ProjectName].Test.XUnit&lt;/code&gt;. &lt;br&gt;
Accordingly, our integration test projects are named &lt;code&gt;[ProjectName].Test.XIntegration&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So it is a two-step process:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;finding the relevant projects&lt;/li&gt;
&lt;li&gt;Running each project using &lt;code&gt;dotnet test&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When I checked the build log, I saw, that it tested one project at a time. If you are familiar with MSBuild it might have been obvious to you, but it took me some time to realize that.&lt;br&gt;
Luckily, &lt;code&gt;dotnet test&lt;/code&gt; using xUnit V2 runs the tests inside of a project (or even inside a solution) in parallel by default. So at least the tests &lt;strong&gt;inside&lt;/strong&gt; of a project will be started in parallel.&lt;/p&gt;

&lt;p&gt;So I started researching how I could run all the test projects in parallel. While reading through a lot of documentation, I always came back to the fact, that all tests inside a solution run in parallel by default. So what about referencing all the test projects in one solution file?&lt;/p&gt;
&lt;h2&gt;
  
  
  Our new way
&lt;/h2&gt;

&lt;p&gt;Despite my worries that maintaining such a solution file would be a lot of effort, I wanted to at least try it to see the performance improvements.&lt;br&gt;
So I wrote a little PowerShell script that would pick up all the unit and integration test projects (just like the MSBuild stuff) and put each type in a single solution. As you will see in the script, I needed to do some string "magic" to get relative paths to the test projects. That was needed because the CI server might run the build in different directories from time to time. So hard-coding the path wasn't an option. Also, hard-coding paths is a bad idea anyhow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$pathobject&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;get-location&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Select-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;@{&lt;/span&gt;&lt;span class="nx"&gt;N&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"PathLength"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nx"&gt;E&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nv"&gt;$pathlength&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$pathobject&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;PathLength&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="nv"&gt;$mylocation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get-location&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Path&lt;/span&gt;&lt;span class="w"&gt; 

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$mylocation&lt;/span&gt;&lt;span class="nx"&gt;/_unittest.sln&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PathType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Leaf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Remove-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_unittest.sln&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="kr"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Test-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$mylocation&lt;/span&gt;&lt;span class="nx"&gt;/_integrationtest.sln&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-PathType&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Leaf&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="n"&gt;Remove-Item&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_integrationtest.sln&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sln&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_unittest&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sln&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_integrationtest&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Get-ChildItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Recurse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*.&lt;/span&gt;&lt;span class="nf"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;XUnit&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;csproj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sln&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_unittest.sln&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FullName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pathlength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Get-ChildItem&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Path&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Recurse&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-Include&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*.&lt;/span&gt;&lt;span class="nf"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;XIntegration&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;csproj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ForEach-Object&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;dotnet&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;sln&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;_integrationtest.sln&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="bp"&gt;$_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FullName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Substring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pathlength&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, I ran the tests in that solution using &lt;code&gt;dotnet test&lt;/code&gt;. The result was amazing. Instead of taking 15 minutes to run, it only took 1 minute and 20 seconds.&lt;br&gt;
That's an improvement worth the name. :-)&lt;/p&gt;

&lt;p&gt;But there was still the effort of keeping the file up-to-date, that scared me. After some more thinking a realized, that I had a script to generate this solution on demand already.&lt;br&gt;
I inserted this script into our build pipeline. It now runs in parallel to our normal build step. This way, the time to run the entire build pipeline stays the same. &lt;br&gt;
And when the pipeline gets to running the tests, it would run &lt;code&gt;dotnet test&lt;/code&gt; on our newly generated solution file which includes all the unit tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;These changes shortened our feedback cycle quite a bit. So I think it is a hackday well spent.&lt;/p&gt;

&lt;p&gt;If you have tips or tricks to shorten the feedback cycle, I would love to hear them. Let me know in the comments.&lt;/p&gt;

</description>
      <category>dotnet</category>
      <category>testing</category>
      <category>ci</category>
    </item>
    <item>
      <title>What's your latest (or favorite) "Can you fix my computer?" story?</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Fri, 12 Jul 2019 05:57:26 +0000</pubDate>
      <link>https://forem.com/n_develop/what-s-your-latest-of-favorite-can-you-fix-my-computer-story-3o0n</link>
      <guid>https://forem.com/n_develop/what-s-your-latest-of-favorite-can-you-fix-my-computer-story-3o0n</guid>
      <description>&lt;p&gt;I'm a developer. Even in my spare time I don't build computers myself. I have a simple&lt;br&gt;
laptop without any customizations (besides DEV.to stickers).&lt;/p&gt;

&lt;p&gt;But still I'm the one guy every non-technical friend or family member asks for help.&lt;br&gt;
To be clear: I know exactly why they do that. It's obvious, that I still know more stuff&lt;br&gt;
about hardware and setting up printers than the non-technical friend. And that's ok.&lt;/p&gt;

&lt;p&gt;But I don't want to complain about the fact, that I get asked to help. It's more about the fact &lt;strong&gt;how&lt;/strong&gt; some people ask for help. Let me tell you about my last time.&lt;br&gt;
We were on a sunday-afternoon trip with friends, at a very old farm that got converted into a museum.&lt;/p&gt;




&lt;p&gt;Friend: Lars, we wanted to ask you something.&lt;/p&gt;

&lt;p&gt;Me: Sure. What's up?&lt;/p&gt;

&lt;p&gt;Friend: My iPhone broke. It fell on the floor. Now the display is broken. Could you try to make a backup?&lt;/p&gt;

&lt;p&gt;Me: At least I can try. Do you have it with you? Just give it to me and I will try at home.&lt;/p&gt;

&lt;p&gt;Friend: But you came by bike, didn't you?&lt;/p&gt;

&lt;p&gt;Me: Yes?!&lt;/p&gt;

&lt;p&gt;Friend: That might be a bit too heavy?&lt;/p&gt;

&lt;p&gt;Me: The iPhone? No, I think I can handle that.&lt;/p&gt;

&lt;p&gt;Friend: But there is also the laptop in the backpack. It would be nice of you could do the backup on our laptop directly, so that it is set up for the next time.&lt;/p&gt;

&lt;p&gt;Me: Oh, ok. Yes, I can do that.&lt;/p&gt;

&lt;p&gt;Friend: Would you also check the anti-virus stuff? I think the license expired. And maybe updates and stuff for all the other important things as well?&lt;/p&gt;

&lt;p&gt;Me: Hmm... sure.&lt;/p&gt;

&lt;p&gt;Friend: But still, isn't it too heavy? The bag with the laptop, the iPhone and the tablet?&lt;/p&gt;

&lt;p&gt;Me: TABLET?&lt;/p&gt;

&lt;p&gt;Friend: My daughter's teacher created some kind of account for some kind of app the kids should use. It would be super awesome if you could install that as well.&lt;/p&gt;

&lt;p&gt;Me: Sure. No problem at all. Maybe something else?&lt;/p&gt;

&lt;p&gt;Friend: No, I think that is it.&lt;/p&gt;




&lt;p&gt;That's my latest story. What's yours? Tell me about your funniest, saddest and/or latest request.&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>fun</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>People I Know - My little PRM</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Thu, 11 Jul 2019 06:41:07 +0000</pubDate>
      <link>https://forem.com/n_develop/people-i-know-my-little-prm-54p2</link>
      <guid>https://forem.com/n_develop/people-i-know-my-little-prm-54p2</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;What's the name of Jane's daughter again?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;or&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I remember, that Michael quit his job. But where did he apply for a new job? I'm sure he told me...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Does this kind of question sound familiar to you? Welcome to my life. :-)&lt;/p&gt;

&lt;p&gt;My memory is not the best. Especially, when it comes to names and stuff. That's why I started using my Google Contacts pretty extensively.&lt;br&gt;
Did you know, that you can store "relationships" in your Google contacts? That's pretty useful.&lt;br&gt;
So on John's contact, I stored a couple of relationships like:&lt;br&gt;
"Wife": "Jane"&lt;br&gt;
"Son": "Michael"&lt;br&gt;
"Daughter": "Rachel"&lt;/p&gt;

&lt;p&gt;That helped me a lot already. If I knew I was going to meet John today, I just checked his contact to remember the names of his kids.&lt;br&gt;
But last time we met, he told me something interesting about his son? What was it again? He broke his arm or something like that.&lt;br&gt;
For this stuff, I used the "Notes" field on the contacts. It's just a free-text field to hold any information like "His son Michael broke a leg".&lt;br&gt;
So if I check his contact (and the "Notes" field) before we meet, my first question could be "Hi John. How is Michael? Is his leg ok again?".&lt;/p&gt;

&lt;p&gt;All that is pretty useful. But I was still missing some features. Because the "Notes" field can grow pretty quickly. And such a big chunk of free text can get confusing.&lt;br&gt;
Wouldn't it be nice, if you could store a list of, let's call it "status updates"? Just a text and a date. These status updates (or "timeline") could be ordered by date.&lt;br&gt;
That's much clearer.&lt;/p&gt;

&lt;p&gt;I thought about using a simple CRM (Customer Relationship Management), but none of them really fit my needs or could easily be hosted on my server. While searching for&lt;br&gt;
a simple CRM, I learned that PRMs are a thing. A PRM is like a CRM, but for personal use (instead of professional/commercial). It's a "Personal Relationship Management".&lt;br&gt;
But I had similar problems with the already existing PRMs. For some of them, it was the price, for others the hosting, etc.&lt;/p&gt;
&lt;h2&gt;
  
  
  I'm a developer. So I built my own PRM.
&lt;/h2&gt;

&lt;p&gt;It is called "People I Know" and you can find the source code on &lt;a href="https://github.com/n-develop/peopleiknow/" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/n-develop" rel="noopener noreferrer"&gt;
        n-develop
      &lt;/a&gt; / &lt;a href="https://github.com/n-develop/peopleiknow" rel="noopener noreferrer"&gt;
        peopleiknow
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      "People I Know" is  a PRM (Personal Relationship Management)
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;People I Know&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;"People I Know" is a PRM
&lt;a rel="noopener noreferrer" href="https://github.com/n-develop/peopleiknowimages/general.png"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Fn-develop%2Fpeopleiknowimages%2Fgeneral.png" alt="Screenshot"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What is a PRM?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;A PRM is a "Personal Relationship Management". On the web, you will also find the name "Personal CRM".
So you can use "People I Know" to store information about the people you know. ;-)&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Is it like my "Contacts" app on my phone?&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;Yes, somehow it is.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;So, why would I use it?&lt;/h3&gt;

&lt;/div&gt;
&lt;p&gt;There were a few reasons for me to build this application. For "normal" contact management, the standard android or iOS
app would be fine. But there was something missing for me.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If you use the before mentioned services, you don't own your data. It is stored somewhere in Apples or Googles Cloud services.&lt;/li&gt;
&lt;li&gt;The standard apps / services do not provide good possibilities to store some kind of timeline / updates for the contacts. You still have the "Notes" field, but it is limited…&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/n-develop/peopleiknow" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Feedback is very welcome. Please let me know what you think. If you see major issues, feel free to contact me or (even better) create an issue on GitHub. &lt;br&gt;
Here are some TODOs that are still on my list:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Photo upload is still missing&lt;/li&gt;
&lt;li&gt;Clean up the JavaScript. Right now it's just one "big" file.&lt;/li&gt;
&lt;li&gt;Improve the mobile user experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see from the TODOs, my frontend skills are... let's call it "limited". ;-) I use &lt;a href="http://bulma.io/" rel="noopener noreferrer"&gt;Bulma&lt;/a&gt; for the styling and pure JavaScript (no framework, no jQuery) for the scripting. The backend is done in ASP.NET Core.&lt;/p&gt;

&lt;p&gt;Again, feedback is very welcome. It's the first "toy project" that I share publicly.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>dotnet</category>
      <category>csharp</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How do you name your tests?</title>
      <dc:creator>Lars Richter</dc:creator>
      <pubDate>Sun, 16 Jun 2019 19:25:51 +0000</pubDate>
      <link>https://forem.com/n_develop/how-do-you-name-your-tests-108c</link>
      <guid>https://forem.com/n_develop/how-do-you-name-your-tests-108c</guid>
      <description>&lt;p&gt;As you might know, I'm a big fan of unit testing and TDD. But I often go back and forth on the naming of my unit tests. There are many popular naming schemes for unit tests. A few of them are:&lt;/p&gt;

&lt;h2&gt;
  
  
  [MethodUnderTest]_[TestedState]_[ExpectedBehavior]
&lt;/h2&gt;

&lt;p&gt;That's a classical naming scheme.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;ReadFrom_FileDoesNotExist_ThrowsException&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  [MethodUnderTest]_[ExpectedBehavior]_[TestedState]
&lt;/h2&gt;

&lt;p&gt;This is pretty similar to the first scheme. Personally, I like this one a little better than the first one. This one reads a little more naturally. For example:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;ReadFromFile_ThrowsException_FileDoesNotExist&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It reads almost like "WriteToFile throws an exception if file does not exist".&lt;/p&gt;

&lt;h2&gt;
  
  
  Should_[ExpectedBehavior]_When_[TestedState]
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Should_ThrowException_When_FileDoesNotExist&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This also reads almost like a "normal" sentence.&lt;/p&gt;

&lt;h2&gt;
  
  
  When_[TestedState]_Expect_[ExpectedBehavior]
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;When_FileDoesNotExist_Expect_WriteToFailToFail&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Given_[Preconditions]_When_[TestedState]_Then_[ExpectedBehavior]
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Given_ReportIsWrittenToFile_When_FileDoesNotExist_Then_ExceptionIsThrown&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  JustDescribeWhatIsGoingOnAndWhatShouldHappen
&lt;/h2&gt;

&lt;p&gt;This is something I fall back to from time to time. In these cases, my test read something like this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;OrderProcessorThrowsAnExceptionInCaseOfMissingCustomer&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These are just a few examples. Feel free to add some more naming schemes in the comments.&lt;/p&gt;

&lt;p&gt;Whats your favorite naming scheme? Are there naming schemes you do not like?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>testing</category>
      <category>unittests</category>
    </item>
  </channel>
</rss>
