<?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>DEV Community: Stephen Solka</title>
    <description>The latest articles on DEV Community by Stephen Solka (@trashhalo).</description>
    <link>https://dev.to/trashhalo</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%2F421878%2F9d30e803-2e55-4935-a99e-079b1d6a87f8.png</url>
      <title>DEV Community: Stephen Solka</title>
      <link>https://dev.to/trashhalo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/trashhalo"/>
    <language>en</language>
    <item>
      <title>Astro Command unlocks the world</title>
      <dc:creator>Stephen Solka</dc:creator>
      <pubDate>Sun, 19 Sep 2021 19:04:43 +0000</pubDate>
      <link>https://dev.to/trashhalo/astro-command-unlocks-the-world-5ehc</link>
      <guid>https://dev.to/trashhalo/astro-command-unlocks-the-world-5ehc</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;This weekend I built a new &lt;a href="https://astro.build/" rel="noopener noreferrer"&gt;Astro&lt;/a&gt; component that lets callers use external commands on their machine to generate html content.&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/trashhalo" rel="noopener noreferrer"&gt;
        trashhalo
      &lt;/a&gt; / &lt;a href="https://github.com/trashhalo/astro-command" rel="noopener noreferrer"&gt;
        astro-command
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      run commands as astro components
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;In the repository there is a hello world demo that is shelling out to python.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Command&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;astro-command&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;---&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Command&lt;/span&gt; &lt;span class="nx"&gt;caller&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;meta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Component.py&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;from python!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;#!/usr/bin/env python 
&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;h1&amp;gt; Hello &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;message&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &amp;lt;/h1&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Props are passed in as a json blob to stdin. Html comes out stdout.&lt;/p&gt;
&lt;h2&gt;
  
  
  Taking it up a notch
&lt;/h2&gt;

&lt;p&gt;Python has a very deep well of libraries that can be useful for astro page generation. One example is &lt;a href="https://matplotlib.org/" rel="noopener noreferrer"&gt;https://matplotlib.org/&lt;/a&gt; a plotting library that can generate beautiful graphs outputting SVG.&lt;/p&gt;

&lt;p&gt;As a quick example I rewrote the hello world py component to output a graph instead.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq4mc0exzrftcm2z9971o.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fq4mc0exzrftcm2z9971o.jpg" alt="Screenshot 2021-09-19 144501"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;h2&gt;
  
  
  Its not just python
&lt;/h2&gt;

&lt;p&gt;Now that you have shell access from Astro you can use any command! The first one I created a custom component for is Pandoc to get access to all the types of content Pandoc can understand. With 12 lines of astro code I unlocked 30+ file formats!&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/trashhalo" rel="noopener noreferrer"&gt;
        trashhalo
      &lt;/a&gt; / &lt;a href="https://github.com/trashhalo/astro-pandoc" rel="noopener noreferrer"&gt;
        astro-pandoc
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      astro component that lets you use pandoc
    &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;Astro Pandoc&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;Astro component for using pandoc to convert content. This allows you to embed any format pandoc supports.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supported formats &lt;a href="https://pandoc.org/MANUAL.html#general-options" rel="nofollow noopener noreferrer"&gt;https://pandoc.org/MANUAL.html#general-options&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/trashhalo/astro-pandoc/blob/main/src/components/Demo.astro" rel="noopener noreferrer"&gt;Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Requires you to have pandoc installed on your machine!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Usage&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="highlight highlight-source-astro notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;---&lt;/span&gt;
&lt;span class="pl-s1"&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt; { &lt;span class="pl-smi"&gt;Pandoc&lt;/span&gt; } &lt;span class="pl-k"&gt;from&lt;/span&gt; &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;astro-pandoc&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;
&lt;span class="pl-c"&gt;---&lt;/span&gt;
&amp;lt;&lt;span class="pl-c1"&gt;Pandoc&lt;/span&gt; &lt;span class="pl-e"&gt;caller&lt;/span&gt;=&lt;span class="pl-pse"&gt;{&lt;/span&gt;&lt;span class="pl-k"&gt;import&lt;/span&gt;.&lt;span class="pl-c1"&gt;meta&lt;/span&gt;.&lt;span class="pl-smi"&gt;url&lt;/span&gt;&lt;span class="pl-pse"&gt;}&lt;/span&gt; &lt;span class="pl-e"&gt;file&lt;/span&gt;=&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;Component.tex&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt; &lt;span class="pl-e"&gt;extraArgs&lt;/span&gt;=&lt;span class="pl-pse"&gt;{&lt;/span&gt;[&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;"&lt;/span&gt;--webtex&lt;span class="pl-pds"&gt;"&lt;/span&gt;&lt;/span&gt;]&lt;span class="pl-pse"&gt;}&lt;/span&gt; /&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Component.tex&lt;/p&gt;
&lt;div class="highlight highlight-text-tex-latex notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;\documentclass&lt;/span&gt;{&lt;span class="pl-c1"&gt;article&lt;/span&gt;}
&lt;span class="pl-k"&gt;\usepackage&lt;/span&gt;{&lt;span class="pl-c1"&gt;amsmath&lt;/span&gt;} &lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;%&lt;/span&gt; for the equation* environment&lt;/span&gt;
&lt;span class="pl-c1"&gt;\begin&lt;/span&gt;{&lt;span class="pl-smi"&gt;document&lt;/span&gt;}
This is a simple math expression &lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;\(&lt;/span&gt;&lt;span class="pl-c1"&gt;\sqrt&lt;/span&gt;{x^2+1}&lt;span class="pl-pds"&gt;\)&lt;/span&gt;&lt;/span&gt; inside text. 
And this is also the same: 
&lt;span class="pl-c1"&gt;\begin&lt;/span&gt;{&lt;span class="pl-smi"&gt;math&lt;/span&gt;}
&lt;span class="pl-c1"&gt;\sqrt&lt;/span&gt;{x^2+1}
&lt;span class="pl-c1"&gt;\end&lt;/span&gt;{&lt;span class="pl-smi"&gt;math&lt;/span&gt;}
but by using another command.

This is a simple math expression without numbering
&lt;span class="pl-s"&gt;&lt;span class="pl-pds"&gt;\[&lt;/span&gt;&lt;span class="pl-c1"&gt;\sqrt&lt;/span&gt;{x^2+1}&lt;span class="pl-pds"&gt;\]&lt;/span&gt;&lt;/span&gt; 
separated from text.

This is also the same:
&lt;span class="pl-c1"&gt;\begin&lt;/span&gt;{&lt;span class="pl-smi"&gt;displaymath&lt;/span&gt;}
&lt;span class="pl-c1"&gt;\sqrt&lt;/span&gt;{x^2+1}
&lt;span class="pl-c1"&gt;\end&lt;/span&gt;{&lt;span class="pl-smi"&gt;displaymath&lt;/span&gt;&lt;/pre&gt;…
&lt;/div&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/trashhalo/astro-pandoc" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Whats next?
&lt;/h2&gt;

&lt;p&gt;There are lots of commands out there. Which one should I connect to astro next?&lt;/p&gt;

</description>
      <category>astro</category>
      <category>python</category>
    </item>
    <item>
      <title>Reddit RSS</title>
      <dc:creator>Stephen Solka</dc:creator>
      <pubDate>Mon, 09 Nov 2020 16:20:48 +0000</pubDate>
      <link>https://dev.to/trashhalo/reddit-rss-44a0</link>
      <guid>https://dev.to/trashhalo/reddit-rss-44a0</guid>
      <description>&lt;p&gt;A tiny project I have been maintaining since April 2020 is an alternative rss feed for Reddit called &lt;a href="https://github.com/trashhalo/reddit-rss"&gt;reddit-rss&lt;/a&gt;. At the time I was getting into using &lt;a href="https://feedly.com/"&gt;Feedly&lt;/a&gt; to read the news and found reddit's native rss feed to be pretty lackluster.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you are interested in using it
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Go to a subreddit or meta feed you like example: &lt;a href="https://www.reddit.com/r/Android/"&gt;https://www.reddit.com/r/Android/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add .json onto the end: &lt;a href="https://www.reddit.com/r/Android.json"&gt;https://www.reddit.com/r/Android.json&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Change the domain name to, reddit.0qz.fun like: &lt;a href="https://reddit.0qz.fun/r/android.json"&gt;https://reddit.0qz.fun/r/android.json&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Subscribe to ^^^ that url in your favorite feed reader.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Useful features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Links directly to the content not to reddit.&lt;/li&gt;
&lt;li&gt;Extracts a readable version of articles and embeds it into the feed.&lt;/li&gt;
&lt;li&gt;Embeds high quality images and videos directly into the feed.&lt;/li&gt;
&lt;li&gt;Extra &lt;a href="https://github.com/trashhalo/reddit-rss#query-params"&gt;query params&lt;/a&gt; to narrow search results to your liking.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How its built
&lt;/h2&gt;

&lt;p&gt;Its golang http server that uses a forked version of &lt;a href="https://github.com/cameronstanley/go-reddit"&gt;cameronstanley/go-reddit&lt;/a&gt; to parse reddit content. Then it inspects each link and decides how to build the feed. Article text content extracted using &lt;a href="https://github.com/go-shiori/go-readability"&gt;go-shiori/go-readability&lt;/a&gt;. Feeds are created using &lt;a href="https://github.com/gorilla/feeds"&gt;gorilla/feeds&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Deployments are managed through &lt;a href="https://github.com/shipitjs/shipit"&gt;shipit&lt;/a&gt; which has been meh. Do not recommend.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>go</category>
      <category>rss</category>
    </item>
    <item>
      <title>📰 Bypass paywalls; read your news on the terminal</title>
      <dc:creator>Stephen Solka</dc:creator>
      <pubDate>Thu, 29 Oct 2020 15:08:02 +0000</pubDate>
      <link>https://dev.to/trashhalo/bypass-paywalls-read-your-news-on-the-terminal-4a6g</link>
      <guid>https://dev.to/trashhalo/bypass-paywalls-read-your-news-on-the-terminal-4a6g</guid>
      <description>&lt;p&gt;Last night I built a new go tool to let me read websites on the command line. &lt;a href="https://github.com/trashhalo/readcli" rel="noopener noreferrer"&gt;Readcli&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;readcli https://medium.com/compass-true-north/go-unit-testing-at-compass-3a7cb85ab54a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fgithub.com%2Ftrashhalo%2Freadcli%2Fraw%2Fmaster%2Fsample.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%2Fgithub.com%2Ftrashhalo%2Freadcli%2Fraw%2Fmaster%2Fsample.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Install (on mac)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;trashhalo/homebrew-brews/readcli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Website Content
&lt;/h2&gt;

&lt;p&gt;Due to the way I am scraping website content not every url will work. I've tested it with medium.com and nytimes.com.&lt;/p&gt;

&lt;p&gt;The algorithm is as follows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use &lt;a href="https://github.com/go-shiori/go-readability" rel="noopener noreferrer"&gt;go-readbility&lt;/a&gt; to download a stripped down version of the website.&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://github.com/JohannesKaufmann/html-to-markdown" rel="noopener noreferrer"&gt;html-to-markdown&lt;/a&gt; to convert the clean html to markdown.&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://github.com/charmbracelet/glamour" rel="noopener noreferrer"&gt;glamour&lt;/a&gt; to render the markdown content.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Bubbletea
&lt;/h2&gt;

&lt;p&gt;The cli interaction is controlled by an elm inspired go library &lt;a href="https://github.com/charmbracelet/bubbletea" rel="noopener noreferrer"&gt;bubbletea&lt;/a&gt;. Its be a absolute dream to work with.&lt;/p&gt;

</description>
      <category>go</category>
      <category>showdev</category>
      <category>news</category>
      <category>elm</category>
    </item>
    <item>
      <title>Embrace the Pipe</title>
      <dc:creator>Stephen Solka</dc:creator>
      <pubDate>Thu, 22 Oct 2020 01:01:55 +0000</pubDate>
      <link>https://dev.to/trashhalo/embrace-the-pipe-4l8b</link>
      <guid>https://dev.to/trashhalo/embrace-the-pipe-4l8b</guid>
      <description>&lt;p&gt;The unix pipe operator is magical.&lt;/p&gt;

&lt;p&gt;As long as you write your application to read input from standard-in and write output to standard-out you can compose your cli tool with thousands of other tools.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scanners
&lt;/h2&gt;

&lt;p&gt;To read one line at a time I like to use &lt;a href="https://golang.org/pkg/bufio/#NewScanner"&gt;bufio.NewScanner&lt;/a&gt;. This uses &lt;a href="https://golang.org/pkg/bufio/#ScanLines"&gt;bufio.ScanLines&lt;/a&gt; to split the reader by new lines.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bufio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewScanner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c"&gt;// Scan returns false when its out of lines&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;// line is a string&lt;/span&gt;
  &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="c"&gt;// if you want bytes in.Bytes() exists too&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Lets build a tool
&lt;/h2&gt;

&lt;p&gt;As an example here is a tiny command line tool I wrote to read urls for images and display them as ascii art images on the terminal.&lt;/p&gt;

&lt;p&gt;Full source &lt;a href="https://gist.github.com/trashhalo/6154a690bd2612ae33c8252d8051eb9f"&gt;https://gist.github.com/trashhalo/6154a690bd2612ae33c8252d8051eb9f&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bufio&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewScanner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stdin&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt;&lt;span class="o"&gt;.&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;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;convertLineToArt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;convertLineToArt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;convertOptions&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DefaultOptions&lt;/span&gt;
    &lt;span class="n"&gt;convertOptions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FixedWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;
    &lt;span class="n"&gt;convertOptions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FixedHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;50&lt;/span&gt;
    &lt;span class="n"&gt;converter&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;convert&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewImageConverter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="k"&gt;range&lt;/span&gt; &lt;span class="n"&gt;converter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Image2ASCIIMatrix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;img&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;convertOptions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&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="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Use Pipe
&lt;/h2&gt;

&lt;p&gt;Now lets combine together a few tools.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt; https://www.reddit.com/r/cats.json | jq &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="s1"&gt;'.data.children[] | .data.url'&lt;/span&gt;|go run .|less
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is saying:  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="https://curl.haxx.se/docs/manual.html"&gt;curl&lt;/a&gt; - download the json for reddit posts on cats. The -A "test" because reddit hates curl user agent so we need to change it.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://stedolan.github.io/jq/manual/v1.6/"&gt;jq&lt;/a&gt; -r 'xyz' - extract the urls for the images from the reddit json.&lt;/li&gt;
&lt;li&gt;go run - run your command!&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://man7.org/linux/man-pages/man1/less.1.html"&gt;less&lt;/a&gt; - Put the output in a little viewer that lets you scroll up and down.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--nWCd4at---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/35os29o562aqz4od5hps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nWCd4at---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/35os29o562aqz4od5hps.png" alt="Alt Text" width="510" height="596"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>unix</category>
      <category>cli</category>
    </item>
    <item>
      <title>Mocking http clients in Go</title>
      <dc:creator>Stephen Solka</dc:creator>
      <pubDate>Wed, 14 Oct 2020 20:54:19 +0000</pubDate>
      <link>https://dev.to/trashhalo/mocking-http-clients-in-go-3l9p</link>
      <guid>https://dev.to/trashhalo/mocking-http-clients-in-go-3l9p</guid>
      <description>&lt;p&gt;If you ever find yourself needing intercept a http client in go, &lt;a href="https://golang.org/pkg/net/http/httptest/#NewServer"&gt;httptest&lt;/a&gt; provides a facility to do just that. &lt;code&gt;httptest.NewServer&lt;/code&gt; lets you stand up a temporary server that you can control in your test case.&lt;/p&gt;

&lt;p&gt;We will use this function as an example of writing unit tests that use httptest.NewServer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;topPostOnSubreddit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://reddit.com/r/%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="n"&gt;dec&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewDecoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="n"&gt;redditData&lt;/span&gt;
    &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dec&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;top&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;findTopPost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&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;top&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Refactor
&lt;/h1&gt;

&lt;p&gt;Let refactor this so we can use httptest.NewServer in our unit tests. The required changes are ...&lt;/p&gt;

&lt;h2&gt;
  
  
  An instance of http client has to be passed in
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;topPostOnSubreddit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://reddit.com/r/%s"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The base of the url has to be configurable by the caller
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;topPostOnSubreddit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fullUrl&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fullUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&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;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Do&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Writing the Test Case
&lt;/h1&gt;

&lt;p&gt;The returned test server has both the client and url available for you to pass to the function. When the function uses the client makes the http call the handlerFunc you defined in your test case is called. This allows you to inject fixture data into the runtime to control unit test behavior.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TestTopPostOnSubreddit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;testing&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;server&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;httptest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandlerFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;enc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c"&gt;// this is where your test fixture goes&lt;/span&gt;
        &lt;span class="n"&gt;testData&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;redditData&lt;/span&gt;&lt;span class="p"&gt;{}&lt;/span&gt;
        &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;enc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;testData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NoError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}))&lt;/span&gt;
    &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Cleanup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Close&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Sprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%s/r/android.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;top&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;topPostOnSubreddit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NoError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;require&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"foobar"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;top&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;h2&gt;
  
  
  Happy Testing!
&lt;/h2&gt;

</description>
      <category>go</category>
      <category>testing</category>
      <category>tdd</category>
    </item>
    <item>
      <title>Testable go with function types</title>
      <dc:creator>Stephen Solka</dc:creator>
      <pubDate>Fri, 09 Oct 2020 00:10:16 +0000</pubDate>
      <link>https://dev.to/trashhalo/testable-go-with-function-types-h99</link>
      <guid>https://dev.to/trashhalo/testable-go-with-function-types-h99</guid>
      <description>&lt;p&gt;A helpful pattern I've picked up writing test cases for go code is called &lt;a href="https://www.thinktocode.com/2018/07/19/ports-and-adapters-architecture/"&gt;Ports and Adapters&lt;/a&gt;. Skipping past long blog post with theory lets get into how this can look in typical go code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TellEveryBobHeSmells&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;emailSvc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getEmailService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getDBConn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`select bobs from db`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bobsEmail&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
        &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;bobsEmail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="c"&gt;// ...&lt;/span&gt;
        &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;emailSvc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendSpiceyEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bobsEmail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="c"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function is going to be very annoying to test. It takes no inputs and reaches out to 2 external services. First to get a list of bobs and another to send emails. Lets refactor this code to be more testable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;sendSpicyEmailFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;
&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;bobFinderFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;tellEveryBobHeSmells&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;findBobs&lt;/span&gt; &lt;span class="n"&gt;bobFinderFn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sendEmail&lt;/span&gt; &lt;span class="n"&gt;sendSpicyEmailFn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;bobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;findBobs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="c"&gt;// ...&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;bobs&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="c"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We created two "ports" to abstract away the side effects of the implementation. &lt;code&gt;sendSpicyEmailFn&lt;/code&gt; and &lt;code&gt;bobFinderFn&lt;/code&gt;. Now our test cases can control the behavior of these functions. You can imagine writing a unit test for this function without even providing a database in the test suite.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="c"&gt;// now we write a public wrapper function that fills in&lt;/span&gt;
&lt;span class="c"&gt;// the adaptors so our callers dont have to care about ports/adaptors&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;TellEveryBobHeSmells&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// note we still passing in dependencies to the adaptors&lt;/span&gt;
    &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getDBConn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;findBobs&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;defaultBobFinderFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;emailSvc&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;getEmailService&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;sendEmail&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;defaultSendSpicyEmailFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emailSvc&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;tellEveryBobHeSmells&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;findBobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;// adaptor implementations&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;defaultBobFinderFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;DB&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;bobFinderFn&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;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;([]&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bobs&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;rows&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;`select bobs from db`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Next&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
            &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="c"&gt;// ...&lt;/span&gt;
            &lt;span class="n"&gt;bobs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&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;bobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;defaultSendSpicyEmailFn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;svc&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;EmailSvc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;sendSpicyEmailFn&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;func&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;error&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;emailSvc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SendSpiceyEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;email&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 that we have cut apart the function into its business logic and external dependencies it is much easier to test. In general I would create a unit test for &lt;code&gt;tellEveryBobHeSmells&lt;/code&gt; and the two adaptors &lt;code&gt;defaultBobFinderFn&lt;/code&gt; and &lt;code&gt;defaultSendSpicyEmailFn&lt;/code&gt; but skip the test case for &lt;code&gt;TellEveryBobHeSmells&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>go</category>
      <category>tdd</category>
      <category>testing</category>
    </item>
    <item>
      <title>Go errors.is cool</title>
      <dc:creator>Stephen Solka</dc:creator>
      <pubDate>Thu, 16 Jul 2020 23:51:35 +0000</pubDate>
      <link>https://dev.to/trashhalo/go-errors-is-cool-eng</link>
      <guid>https://dev.to/trashhalo/go-errors-is-cool-eng</guid>
      <description>&lt;p&gt;In 1.13 go introduced a new function on the errors package. &lt;code&gt;Is&lt;/code&gt;. This lets you ask if the error you are holding is the same as a predefined defined error or the error wraps the predefined error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;var&lt;/span&gt; &lt;span class="n"&gt;BadThingErr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"omg something happened"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;canReturnErr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;func&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;

&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;canReturnErr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;BadThingErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"that bad thing that can happen happened"&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;Ive been using Is since it was released but there is a small detail of the the documentation that I originally missed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;An error is considered to match a target if it is equal to that target or if it implements a method Is(error) bool such that Is(target) returns true.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;myCoolErr&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;errCode&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;myCoolErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;me&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;myCoolErr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="no"&gt;false&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;me&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;errCode&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This lets an error control the implementation of errors.Is when comparing equality of the error object. Neat!&lt;/p&gt;

&lt;h2&gt;
  
  
  How does this work?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;interface&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Is&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;target&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="no"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/golang/go/blob/43c6ada84c6ef47e3b61646d2f2e7f6b7264929d/src/errors/wrap.go#L39-L60"&gt;https://github.com/golang/go/blob/43c6ada84c6ef47e3b61646d2f2e7f6b7264929d/src/errors/wrap.go#L39-L60&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the source code of the errors package there is a cool trick. This is asking if the err object complies with an anonymous interface if it does then the go compiler allows you to call the method on the anonymous interface!&lt;/p&gt;

&lt;p&gt;I have seen casts and casts to interfaces but never casts to an interface defined in the cast expression.&lt;/p&gt;

&lt;p&gt;This pattern seems most useful if your target interface is a single method and only used in one place. The risk is too high for the anonymous interface definitions to diverge if its used in multiple source locations. At that point I'd recommend defining a proper &lt;code&gt;type Xxx interface&lt;/code&gt;&lt;/p&gt;

</description>
      <category>go</category>
      <category>errors</category>
    </item>
    <item>
      <title>Managing up through calendar stalking</title>
      <dc:creator>Stephen Solka</dc:creator>
      <pubDate>Wed, 08 Jul 2020 23:17:43 +0000</pubDate>
      <link>https://dev.to/trashhalo/managing-up-through-calendar-stalking-1hel</link>
      <guid>https://dev.to/trashhalo/managing-up-through-calendar-stalking-1hel</guid>
      <description>&lt;p&gt;Working at start-ups can be a pretty crazy experience. When everyone is running hard, one of the first things to fail is effective communication. One trick that can help plug the gap of missing information is invoking the dark arts of &lt;em&gt;Calendar Stalking&lt;/em&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Calendar Stalking
&lt;/h1&gt;

&lt;p&gt;In many organizations google calendar is configured to allow anyone to see anyone else's calendar. Calendars are filled with useful information. Calendar stalking is the art of using that data to make sense of the swirling world around you.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to see someone's calendar?
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;On your computer, open Google Calendar.&lt;/li&gt;
&lt;li&gt;On the left click Search for people.&lt;/li&gt;
&lt;li&gt;Start typing someone’s name and choose the person you want to meet with.&lt;/li&gt;
&lt;li&gt;At the top left, click Create Add.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If they were evil and configured their calendar only says Busy/Free see appendix at the end.&lt;/p&gt;

&lt;p&gt;Start with your manager's calendar. Information tends to get lost or distorted when moving between levels. Your manager's manager and up the chain are all great sources of information.&lt;/p&gt;

&lt;p&gt;Ignore all the repeating meetings. Ignore the one on ones. 80% of the meetings are boring fluff everyone has. You are looking for one off meetings talking about topics that you do not know much about. Events that have not been accepted yet are especially good because often they have recently been created.&lt;/p&gt;

&lt;p&gt;In the calendar event there may be an agenda attached. Be sure to read that. Extra information can sometimes be found in your office wiki using words in from the subject or description of the event.&lt;/p&gt;

&lt;h2&gt;
  
  
  What do I do with this new information?
&lt;/h2&gt;

&lt;p&gt;Is the meeting on an interesting topic and you wish you were invited to participate? Great, ask if you can come!&lt;/p&gt;

&lt;p&gt;You not being invited was probably not an intentional slight. People are more than willing to let more come to meetings if they ask. Being present and contributing at the meetings increases your knowledge and presence at your company.&lt;/p&gt;

&lt;p&gt;Meeting with an ambiguous agenda? Sometimes asking the organizer can help get more information.&lt;/p&gt;

&lt;p&gt;"I noticed this meeting on your calendar. I was wondering if you might share what it is about?" There are of course limits to prodding. Don't ask people ops why they are meeting with someone suddenly on a Friday. In general people are pretty open to questions.&lt;/p&gt;

&lt;p&gt;Your manager is a good resource of extra context for meetings they are in. Your one on one is a perfect time to talk about things you have observed around the office and on calendars. Most things going on at your job are not secret. Once you ask people open up and tell you all about what's happening.&lt;/p&gt;

&lt;h1&gt;
  
  
  Knowledge is power
&lt;/h1&gt;

&lt;p&gt;The more you understand the world around you the more effective you will be in your job. You can get a sense for what is coming down the pipeline, not just the tickets you have been assigned. Stop waiting for people to notice you and invite you to their meeting. Find the meetings you want to go to and ask!&lt;/p&gt;

&lt;h2&gt;
  
  
  Appendix: Workaround for free/busy calendars
&lt;/h2&gt;

&lt;p&gt;Sometimes people configure their calendars to hide the names of meetings they are going to. One workaround for this is if you can figure out who this person meets with on a regular basis then you can can look at that persons calendar and look for meetings that overlap.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>management</category>
      <category>startup</category>
    </item>
    <item>
      <title>How to ship more points</title>
      <dc:creator>Stephen Solka</dc:creator>
      <pubDate>Thu, 02 Jul 2020 01:56:28 +0000</pubDate>
      <link>https://dev.to/trashhalo/how-to-ship-more-points-5hl1</link>
      <guid>https://dev.to/trashhalo/how-to-ship-more-points-5hl1</guid>
      <description>&lt;p&gt;A lot can impact your velocity number. The kinds of tickets you have been taking, your mood, how you approach your work, etc. etc. This article is about what I do to consistently deliver a high number of points.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why do you want this?
&lt;/h1&gt;

&lt;p&gt;Establish why this ticket was prioritized. What do we hope to accomplish? Often the &lt;em&gt;why&lt;/em&gt; is lost in a sea of technical requirements, product asks and commentary with stakeholders. To find the true why it may require you to follow a technique called &lt;a href="https://untools.co/abstraction-laddering"&gt;Abstract Laddering&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract laddering
&lt;/h2&gt;

&lt;p&gt;This is a thought process that helps you move from concrete requirements to abstract asks and back down. To identify the why. Given a position on the ladder ask why to move up a level and how to move down a level.&lt;/p&gt;

&lt;p&gt;Lets say you are assigned a ticket that has the title "Make the can opener more appealing". You could start doing research on what makes can openers appealing and prototyping options. OR you could ask &lt;em&gt;why&lt;/em&gt;. Why do I have this ticket? If its not defined in the ticket why, ask your tech lead or product manager Why?&lt;/p&gt;

&lt;p&gt;You might get an answer "We want to design a better can opener". Great! Now you have options. Appealing is just one way to improve the can opener. Some of these options may even cost less points to deliver to production.&lt;/p&gt;

&lt;h2&gt;
  
  
  When why isn't enough
&lt;/h2&gt;

&lt;p&gt;Perhaps you didn't get to any good options when you learned your company really wants to "design a better can opener". Sometimes to find the easy way to deliver the ticket you have to go deeper.&lt;/p&gt;

&lt;p&gt;Push past the initial answer and continue to ask questions until you can move up a level of abstraction. Why does your company care about designing a better can opener? This can lead to new options to deliver.&lt;/p&gt;

&lt;p&gt;Assume you found out that your company only cares about can openers because "they want to help people get soup out of their cans". Whole new options open up for delivering your ticket! You could even add a pull tab to the top of cans!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RgkGfCIe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sv9de4vc27bh2bj5kru5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RgkGfCIe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/sv9de4vc27bh2bj5kru5.png" alt="Can Opener Diagram" width="880" height="495"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The point of all this is to identify an implementation that gets your company what its looking for but costs significantly less than the original estimate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Negotiation
&lt;/h2&gt;

&lt;p&gt;Keep in mind this is a negotiation process with your peers. If at the end of the sprint your tickets are done but no one is happy then you have lost. Make sure you really are building the right thing but do not feel constrained by the tickets you are given.&lt;/p&gt;

&lt;h1&gt;
  
  
  What is preventing me from shipping today?
&lt;/h1&gt;

&lt;p&gt;In the middle phase of a long project I often find myself lost. Unclear what is left and how much longer its going to take to get there. A useful mantra to adopt to find your way out is to ask yourself "Could I ship this feature today? If not why?".&lt;/p&gt;

&lt;p&gt;Work through the set of changes in your mind that are preventing you from completing this project. Focus your energy on the work that is truly "blocking" from complete. Regularly ask yourself this question.&lt;/p&gt;

&lt;h1&gt;
  
  
  Cut scope
&lt;/h1&gt;

&lt;p&gt;Tickets are never the exact minimum viable product change needed to accomplish a goal. There are often miscellaneous changes tacked on. Sometimes those extra changes drastically increase the cost of the ticket.&lt;/p&gt;

&lt;p&gt;Product managers are often willing to negotiate with engineers. Often through conversations and a keen eye you can split large tickets apart and put the lower priority work back into the backlog.&lt;/p&gt;

&lt;h1&gt;
  
  
  Points are made up and they don't matter
&lt;/h1&gt;

&lt;p&gt;The number of points you complete in a sprint mostly doesn't matter. Your primary purpose as an engineer is to deliver value to your organization. The value you bring is though building or changing software. Focusing on these techniques can help you avoid getting bogged down building low value changes over extended periods of time.&lt;/p&gt;

</description>
      <category>agile</category>
      <category>productivity</category>
      <category>startup</category>
    </item>
  </channel>
</rss>
