<?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: Pejman Yaghmaie</title>
    <description>The latest articles on DEV Community by Pejman Yaghmaie (@yaghmaie).</description>
    <link>https://dev.to/yaghmaie</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%2F1180673%2F9a79126e-884a-494b-bee5-44cf8948b748.jpeg</url>
      <title>DEV Community: Pejman Yaghmaie</title>
      <link>https://dev.to/yaghmaie</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yaghmaie"/>
    <language>en</language>
    <item>
      <title>Reuse tests with multiple implementations of a trait in Rust by rstest</title>
      <dc:creator>Pejman Yaghmaie</dc:creator>
      <pubDate>Mon, 05 Feb 2024 18:34:49 +0000</pubDate>
      <link>https://dev.to/yaghmaie/reuse-tests-with-multiple-implementations-of-a-trait-in-rust-by-rstest-5bm1</link>
      <guid>https://dev.to/yaghmaie/reuse-tests-with-multiple-implementations-of-a-trait-in-rust-by-rstest-5bm1</guid>
      <description>&lt;p&gt;Every programming language has its own ways to let us abstract details away, and in &lt;a href="https://www.rust-lang.org/"&gt;Rust&lt;/a&gt; it's &lt;a href="https://doc.rust-lang.org/book/ch10-02-traits.html"&gt;traits&lt;/a&gt; that give us the ability.&lt;br&gt;
Traits in Rust enable lots of flexibilities such as extending functionalities of a data type or dynamic dispatching.&lt;br&gt;
One of them that we'll be focusing on in this article is expressing common behavior of multiple implementations under the interface defined by a trait.&lt;/p&gt;

&lt;p&gt;A perfect example of such implementations is a data store. We expect a data store to have the exact same behavior whether it's storing the data in memory or some advanced highly scalable database in the cloud.&lt;/p&gt;

&lt;p&gt;On the one hand, we have traits to help us with coordination between the data stores to implement the common interface defined by a trait. On the other hand, while different implementations share the same trait interface, their internal behavior might differ. Therefore, we need to have a good test coverage in order to ensure that all the different implementations behave consistently from the perspective of their dependents.&lt;/p&gt;

&lt;p&gt;To effectively achieve such test coverage without duplicating code or writing error prone boilerplate, we need test fixtures which Rust does not support by default. However, we can use &lt;a href="https://crates.io/crates/rstest"&gt;&lt;strong&gt;rstest&lt;/strong&gt;&lt;/a&gt; which is a great fixture-based test framework for Rust.&lt;/p&gt;

&lt;p&gt;In this article we'll practice test driven development and use &lt;strong&gt;rstest&lt;/strong&gt; to accomplish:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implementing a data store trait in two different ways&lt;/li&gt;
&lt;li&gt;Maintaining a good test coverage and ensuring different implementations behave the same&lt;/li&gt;
&lt;li&gt;Keeping code and test duplication minimized&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;TL;DR you can find the code &lt;a href="https://gitlab.com/yaghmaie/rust-test-reuse"&gt;here on GitLab&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now let's get started by creating a new package.&lt;/p&gt;
&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;

&lt;p&gt;First of all we need to run a few familiar commands to make a new package for our example and add &lt;a href="https://crates.io/crates/rstest"&gt;&lt;strong&gt;rstest&lt;/strong&gt;&lt;/a&gt; and &lt;a href="https://crates.io/crates/rstest_reuse"&gt;&lt;strong&gt;rstest_reuse&lt;/strong&gt;&lt;/a&gt; to the dev dependencies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;rstest&lt;/strong&gt; lets us inject test dependencies (like the data stores we talked about earlier) into different test functions and &lt;strong&gt;rstest_reuse&lt;/strong&gt; makes it possible to apply the same test template on multiple test functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;cargo new rstest_multi_impl_trait 
     Created library &lt;span class="sb"&gt;`&lt;/span&gt;rstest_multi_impl_trait&lt;span class="sb"&gt;`&lt;/span&gt; package
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;rstest_multi_impl_trait
&lt;span class="nv"&gt;$ &lt;/span&gt;cargo add rstest rstest_reuse &lt;span class="nt"&gt;--dev&lt;/span&gt;
    Updating crates.io index
      Adding rstest v0.18.2 to dev-dependencies.
             Features:
             + async-timeout
      Adding rstest_reuse v0.6.0 to dev-dependencies.
    Updating crates.io index
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;Let's say we want to manage some &lt;code&gt;Fruit&lt;/code&gt;s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Fruit&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&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;In order to do that, we'll need a &lt;code&gt;FruitStore&lt;/code&gt; to &lt;code&gt;add&lt;/code&gt; or &lt;code&gt;remove&lt;/code&gt; our &lt;code&gt;Fruit&lt;/code&gt;s for us and also be able to &lt;code&gt;find&lt;/code&gt; them. Occasionally, we need to know how many &lt;code&gt;Fruit&lt;/code&gt;s we have in the store . So, the store should be able to &lt;code&gt;count&lt;/code&gt; them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;FruitStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;AddError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;DeleteError&lt;/span&gt;&lt;span class="o"&gt;&amp;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;Of course, if we &lt;code&gt;add&lt;/code&gt; some &lt;code&gt;Fruit&lt;/code&gt; twice or try to &lt;code&gt;remove&lt;/code&gt; one that doesn't exist, then we expect an error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;AddError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;DuplicateId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;DeleteError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;IdNotFound&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;
  
  
  &lt;a href="https://gitlab.com/yaghmaie/rust-test-reuse/-/commit/62bd47429adb93a25153e8bd4a83536437996cde"&gt;&lt;code&gt;VecFruitStore&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;We want to keep things simple and that's why we don't want to use a real database or a cloud storage service to implement our &lt;code&gt;FruitStore&lt;/code&gt;. Instead we're going to use a &lt;code&gt;Vec&amp;lt;Fruit&amp;gt;&lt;/code&gt; as our storage backend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;VecFruitStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;VecFruitStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;VecFruitStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And surely &lt;code&gt;VecFruitStore&lt;/code&gt; has to implement &lt;code&gt;FruitStore&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;VecFruitStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fruit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;AddError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;DeleteError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&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;h2&gt;
  
  
  &lt;a href="https://gitlab.com/yaghmaie/rust-test-reuse/-/commit/a688aa9ae2d1d051a963b53af1654e270e5d5446"&gt;Empty &lt;code&gt;VecFruitStore&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;It's always a better idea to keep things simple at the start and begin with coding the most simple functions with the most basic inputs. Speaking of data stores, the most simple state of them is surely when they're empty.&lt;br&gt;
Probably the simplest function to test on an empty data store is &lt;code&gt;count&lt;/code&gt; since it just needs to return &lt;code&gt;0&lt;/code&gt;. That's where we start then. Needless to say, we're going to write a test before writing any code and follow &lt;a href="https://martinfowler.com/bliki/TestDrivenDevelopment.html"&gt;&lt;strong&gt;TDD&lt;/strong&gt;&lt;/a&gt; as mentioned before.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;empty_store_counts_zero&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;VecFruitStore&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.count&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;If we run our new test with &lt;code&gt;cargo test --lib&lt;/code&gt; it fails because &lt;code&gt;count&lt;/code&gt; is not implemented yet and just includes a placeholder &lt;code&gt;todo!()&lt;/code&gt;; So, this is our &lt;a href="https://martinfowler.com/bliki/TestDrivenDevelopment.html"&gt;&lt;strong&gt;Red&lt;/strong&gt;&lt;/a&gt; step:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;running 1 test
test tests::empty_store_counts_zero ... FAILED

failures:

---- tests::empty_store_counts_zero stdout ----
thread 'tests::empty_store_counts_zero' panicked at src/lib.rs:41:9:
not yet implemented
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The easiest change to make the test pass is simply returning &lt;code&gt;0&lt;/code&gt; from &lt;code&gt;count&lt;/code&gt; and that's what we do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&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 plaintext"&gt;&lt;code&gt;running 1 test
test tests::empty_store_counts_zero ... ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next simple thing to test is trying to &lt;code&gt;find&lt;/code&gt; a &lt;code&gt;Fruit&lt;/code&gt; in an empty store. Certainly, an empty store has nothing to &lt;code&gt;find&lt;/code&gt; for you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;empty_store_finds_none&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;VecFruitStore&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;fruit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fruit&lt;/span&gt;&lt;span class="nf"&gt;.is_none&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And making it pass&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Fruit&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;None&lt;/span&gt;
&lt;span class="p"&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 plaintext"&gt;&lt;code&gt;running 2 tests
test tests::empty_store_counts_zero ... ok
test tests::empty_store_finds_none ... ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we &lt;code&gt;remove&lt;/code&gt; a &lt;code&gt;Fruit&lt;/code&gt;, we should get a delete error.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;empty_store_remove_gives_id_not_found_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;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;VecFruitStore&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;matches!&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;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;DeleteError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IdNotFound&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;usize&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;DeleteError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;DeleteError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IdNotFound&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;running 3 tests
test tests::empty_store_finds_none ... ok
test tests::empty_store_remove_gives_id_not_found_error ... ok
test tests::empty_store_counts_zero ... ok
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a href="https://gitlab.com/yaghmaie/rust-test-reuse/-/commit/d55510a5960a014ac7c221b5ca444dd89dde8317"&gt;Refactoring tests&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;In the first line of every test we're creating a new &lt;code&gt;VecFruitStore&lt;/code&gt; right now. This is not ideal because:&lt;/p&gt;

&lt;p&gt;1) We have duplication&lt;br&gt;
2) How do we reuse our tests if we had another implementation of &lt;code&gt;FruitStore&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;To fix these, we need fixture-based tests so it's time for &lt;a href="https://crates.io/crates/rstest"&gt;&lt;strong&gt;rstest&lt;/strong&gt;&lt;/a&gt; to enter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rstest&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="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to turn our tests from something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[test]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;empty_store_counts_zero&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;VecFruitStore&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.count&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;To&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[rstest]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;empty_store_counts_zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.count&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;Instead of initializing a new &lt;code&gt;FruitStore&lt;/code&gt; in each test case, let's just receive one from somewhere and focus on the testing part. This way we have a test that doesn't care about what exactly is the &lt;code&gt;store&lt;/code&gt; that it's evaluating as long is it implements the &lt;code&gt;FruitStore&lt;/code&gt; trait.&lt;/p&gt;

&lt;p&gt;The next question would be: How the tests receive their &lt;code&gt;store&lt;/code&gt; to run?&lt;br&gt;
Well, &lt;a href="https://crates.io/crates/rstest"&gt;&lt;strong&gt;rstest&lt;/strong&gt;&lt;/a&gt; makes this is easy. All we need to do is throwing in a fixture function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[fixture]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;VecFruitStore&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&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;Applied to all the tests, the result should be the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;DeleteError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FruitStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;VecFruitStore&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;rstest&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="nd"&gt;#[fixture]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;store&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;VecFruitStore&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;#[rstest]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;empty_store_counts_zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.count&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;#[rstest]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;empty_store_finds_none&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;fruit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fruit&lt;/span&gt;&lt;span class="nf"&gt;.is_none&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;#[rstest]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;empty_store_remove_gives_id_not_found_error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nd"&gt;assert!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;matches!&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;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;DeleteError&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;IdNotFound&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;running 3 tests
test tests::empty_store_counts_zero ... ok
test tests::empty_store_remove_gives_id_not_found_error ... ok
test tests::empty_store_finds_none ... ok

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  &lt;a href="https://gitlab.com/yaghmaie/rust-test-reuse/-/commit/619ad70a5b0161fa4b7b65de67e6da67e50a7bf0"&gt;Get ready to add another &lt;code&gt;FruitStore&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;There's still a catch. If we add another implementation of &lt;code&gt;FruitStore&lt;/code&gt; like a &lt;code&gt;HashMapFruitStore&lt;/code&gt; which uses a &lt;code&gt;HashMap&amp;lt;usize, Fruit&amp;gt;&lt;/code&gt; as storage backend, then how can it be tested by the existing tests? Since the fixture function we wrote earlier only returns a &lt;code&gt;VecFruitStore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Such situations can easily be handled by &lt;a href="https://crates.io/crates/rstest_reuse"&gt;&lt;strong&gt;rstest_reuse&lt;/strong&gt;&lt;/a&gt;. Just remember that you have to add the following code at the &lt;strong&gt;top of the crate&lt;/strong&gt; (not the module).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;rstest_reuse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First we need to get rid of the fixture function and replace it with a &lt;code&gt;template&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[template]&lt;/span&gt;
&lt;span class="nd"&gt;#[rstest]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fruit_store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&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;Secondly, we should change the &lt;code&gt;#[rstest]&lt;/code&gt; on the tests to &lt;code&gt;#[apply(fruit_store)]&lt;/code&gt;. This way &lt;strong&gt;rstest&lt;/strong&gt; can recognize what template has to be applied on which tests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[apply(fruit_store)]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;empty_store_counts_zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.count&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;Finally, we use &lt;code&gt;case&lt;/code&gt; on the template to inject our desired &lt;code&gt;FruitStore&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[template]&lt;/span&gt;
&lt;span class="nd"&gt;#[rstest]&lt;/span&gt;
&lt;span class="nd"&gt;#[case::vec_fruit_store(VecFruitStore::new())]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fruit_store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;#[case]&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&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;
  
  
  &lt;a href="https://gitlab.com/yaghmaie/rust-test-reuse/-/commit/28a1f6a14cdbdeb917f746f564614eec43b7e902"&gt;Adding another &lt;code&gt;FruitStore&lt;/code&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;As you have already guessed adding another &lt;code&gt;FruitStore&lt;/code&gt; implementation to this test machine is as easy as adding another &lt;code&gt;case&lt;/code&gt; on the template like &lt;code&gt;#[case::hashmap_fruit_store(HashMapFruitStore::new())]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[template]&lt;/span&gt;
&lt;span class="nd"&gt;#[rstest]&lt;/span&gt;
&lt;span class="nd"&gt;#[case::vec_fruit_store(VecFruitStore::new())]&lt;/span&gt;
&lt;span class="nd"&gt;#[case::hashmap_fruit_store(HashMapFruitStore::new())]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;fruit_store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;#[case]&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&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;But another problem pops up:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;running 6 tests
test tests::empty_store_counts_zero::case_1_vec_fruit_store ... ok
test tests::empty_store_counts_zero::case_2_hashmap_fruit_store ... FAILED
test tests::empty_store_finds_none::case_1_vec_fruit_store ... ok
test tests::empty_store_finds_none::case_2_hashmap_fruit_store ... FAILED
test tests::empty_store_remove_gives_id_not_found_error::case_1_vec_fruit_store ... ok
test tests::empty_store_remove_gives_id_not_found_error::case_2_hashmap_fruit_store ... FAILED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine if we had 100 test cases, then we had to complete the implementation and get all of them passed in order to get a green build. However, the &lt;code&gt;case&lt;/code&gt;s also work if on top of the tests. Accordingly, we can add the new &lt;code&gt;case&lt;/code&gt; one by one on them until all are covered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[apply(fruit_store)]&lt;/span&gt;
&lt;span class="nd"&gt;#[case::hashmap_fruit_store(HashMapFruitStore::new())]&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;empty_store_counts_zero&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;FruitStore&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="nf"&gt;.count&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;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;Nothing can grow our confidence for our work like a great test coverage. Testing should not be something totally separate from our everyday workflow or an afterthought. In this article, we went through an example of implementing a trait of a data store with the same tests with help of &lt;a href="https://crates.io/crates/rstest"&gt;&lt;strong&gt;rstest&lt;/strong&gt;&lt;/a&gt;. I hope you find it useful.&lt;/p&gt;

&lt;p&gt;I'm going to leave the code at the current stage so you'd have something to practice on. So please take a copy of this code from &lt;a href="https://gitlab.com/yaghmaie/rust-test-reuse"&gt;here&lt;/a&gt; and start adding more test cases and also continue the implementation.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>testing</category>
      <category>rstest</category>
      <category>tdd</category>
    </item>
    <item>
      <title>Development container &amp; Gitlab CI - Build better and faster</title>
      <dc:creator>Pejman Yaghmaie</dc:creator>
      <pubDate>Sun, 22 Oct 2023 17:32:08 +0000</pubDate>
      <link>https://dev.to/yaghmaie/development-container-gitlab-ci-build-better-and-faster-16ob</link>
      <guid>https://dev.to/yaghmaie/development-container-gitlab-ci-build-better-and-faster-16ob</guid>
      <description>&lt;p&gt;One of the benefits of an automated build pipeline is reproducible outcomes. This alone has several other benefits such as more reasonable results, and less surprises. Ideally we want the exact same stuff every time we run the pipeline for a specific version of our code regardless of where we are executing it.&lt;/p&gt;

&lt;p&gt;It is encouraged to version control everything that affects the final outcome. Like the code, third party dependencies, all the OS packages required to build our software, the pipeline itself, etc. Additionally, each run has to be isolated to avoid interference of all the side effects caused by the other runs or pipelines.&lt;/p&gt;

&lt;p&gt;This approach clearly increases our confidence in the result. But, one of it's biggest caveats is much slower build times. Definitely, it is exhaustive to prepare the environment and commence the build from scratch each time. So, the challenge is to have the predictable outcomes while having it fast.&lt;/p&gt;

&lt;p&gt;In this post we want to exercise this challenge using &lt;a href="https://docs.gitlab.com/ee/ci/"&gt;Gitlab CI&lt;/a&gt; and &lt;a href="https://www.docker.com/"&gt;Docker&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The build environment
&lt;/h2&gt;

&lt;p&gt;The environment that we run our build is the base layer of all the other operations. The compiler, packaging tools, operating system and all that is installed on it are all examples of what makes that environment. Tools like Docker make it a lot easy to prepare our desired environment as a &lt;a href="https://www.docker.com/resources/what-container/"&gt;container&lt;/a&gt;. We can put together all the bits and pieces with a &lt;a href="https://docs.docker.com/engine/reference/builder/"&gt;Dockerfile&lt;/a&gt;, or pick a precooked one (like Node.js 20 on Debian 10) off &lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We want to defined our pipeline with Gitlab CI in a way that this container is efficiently created with each check-in and used through the rest of the steps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;To give it a bit of a context, we will write a program in &lt;a href="https://www.rust-lang.org/"&gt;Rust&lt;/a&gt; which calls a &lt;a href="https://grpc.io"&gt;gRPC&lt;/a&gt; API and in the CI pipeline we will build and test it. We are going to start with an empty project and develop it step by step.&lt;/p&gt;

&lt;p&gt;You can see the taken steps and resulting pipelines all in &lt;a href="https://gitlab.com/yaghmaie/development-container"&gt;&lt;strong&gt;Development Container repository&lt;/strong&gt;&lt;/a&gt;. Each commit represents a single step and it's changes. Also take a look at the &lt;a href="https://gitlab.com/yaghmaie/development-container/-/pipelines"&gt;&lt;strong&gt;Pipelines&lt;/strong&gt;&lt;/a&gt; ran during this exercise.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: &lt;a href="https://gitlab.com/yaghmaie/development-container/-/commit/e9e9d066b5eaba3815ad65f83c68ea667aff6c05"&gt;Hello, World&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;First, we create a new project with &lt;code&gt;cargo new grpccaller&lt;/code&gt; and we add a small hello world test in &lt;a href="https://gitlab.com/yaghmaie/development-container/-/blob/e9e9d066b5eaba3815ad65f83c68ea667aff6c05/src/main.rs"&gt;&lt;code&gt;main.rs&lt;/code&gt;&lt;/a&gt;. We add a single stage to run &lt;code&gt;cargo test&lt;/code&gt; in our &lt;a href="https://gitlab.com/yaghmaie/development-container/-/blob/e9e9d066b5eaba3815ad65f83c68ea667aff6c05/.gitlab-ci.yml"&gt;pipeline&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;At this point we just want to make sure that our CI server is able to pick up jobs, and we are able to run some unit tests in the pipeline to get feedback on our changes to the code. Keeping the progress in tiny steps helps to catch issues earlier when they are not too complex.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .gitlab-ci.yml&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

&lt;span class="s"&gt;cargo:test:&lt;/span&gt;
  &lt;span class="s"&gt;stage&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rust:alpine3.18&lt;/span&gt;
  &lt;span class="s"&gt;script&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cargo test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no need to have the &lt;strong&gt;Development Container&lt;/strong&gt; yet. We can use the existing &lt;a href="https://hub.docker.com/_/rust"&gt;official Rust docker image&lt;/a&gt; off the shelf.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: &lt;a href="https://gitlab.com/yaghmaie/development-container/-/commit/f972575a11ca76ff1faa52f5ffccf9a1fa491aee"&gt;Add gRPC dependencies&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Now we add the dependencies we need for implementing the gRPC client to &lt;a href="https://gitlab.com/yaghmaie/development-container/-/blob/f972575a11ca76ff1faa52f5ffccf9a1fa491aee/Cargo.toml"&gt;&lt;code&gt;Cargo.toml&lt;/code&gt;&lt;/a&gt; then push it so the CI server run tests for us and make sure all is good. But in software development nothing always goes as planned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# Cargo.toml&lt;/span&gt;

&lt;span class="nn"&gt;[dependencies]&lt;/span&gt;
&lt;span class="py"&gt;prost&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.12.1"&lt;/span&gt;
&lt;span class="nn"&gt;tokio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.33.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;features&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"macros"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"rt-multi-thread"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;tonic&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.10.2"&lt;/span&gt;

&lt;span class="nn"&gt;[build-dependencies]&lt;/span&gt;
&lt;span class="py"&gt;tonic-build&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"0.10.2"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like &lt;code&gt;cargo&lt;/code&gt; cannot finish the build because of some packages missing from the &lt;a href="https://hub.docker.com/_/rust"&gt;official Rust docker image&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cannot find crti.o: No such file or directory
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: &lt;a href="https://gitlab.com/yaghmaie/development-container/-/commit/e87efc7d65e43ad553c2e964a0abedd6f66d1102"&gt;Install required packages for build&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;So, as the next step we will easily add some lines to the &lt;a href="https://gitlab.com/yaghmaie/development-container/-/blob/e87efc7d65e43ad553c2e964a0abedd6f66d1102/.gitlab-ci.yml"&gt;CI file&lt;/a&gt; and fix it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .gitlab-ci.yml&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

&lt;span class="s"&gt;cargo:test:&lt;/span&gt;
  &lt;span class="s"&gt;stage&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rust:alpine3.18&lt;/span&gt;
  &lt;span class="s"&gt;script&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apk add musl-dev&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apk add protoc&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cargo test&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You may have noticed what we are doing is not the best. Every time the pipeline runs it installs &lt;code&gt;musl-dev&lt;/code&gt; and &lt;code&gt;protoc&lt;/code&gt;, fetches all the dependencies, and compiles the whole thing from start. It can be a lot more efficient but we rather make it work first then worry about making it right and fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: &lt;a href="https://gitlab.com/yaghmaie/development-container/-/commit/d06e1940e8f86688cd7a8400dc711ff2ca9f5478"&gt;Call grpcbin SayHello&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;We want to call the &lt;a href="https://github.com/moul/grpcbin"&gt;grpcbin&lt;/a&gt; hello service to say hello. We will use the instance hosted by &lt;a href="https://k6.io/"&gt;k6&lt;/a&gt; at &lt;a href="https://grpcbin.test.k6.io/"&gt;https://grpcbin.test.k6.io/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We add &lt;a href="https://gitlab.com/yaghmaie/development-container/-/blob/d06e1940e8f86688cd7a8400dc711ff2ca9f5478/proto/helloworld.proto"&gt;&lt;code&gt;helloworld.proto&lt;/code&gt;&lt;/a&gt;, a &lt;a href="https://gitlab.com/yaghmaie/development-container/-/blob/d06e1940e8f86688cd7a8400dc711ff2ca9f5478/build.rs"&gt;&lt;code&gt;build.rs&lt;/code&gt;&lt;/a&gt;, and apply changes to &lt;a href="https://gitlab.com/yaghmaie/development-container/-/blob/d06e1940e8f86688cd7a8400dc711ff2ca9f5478/src/main.rs"&gt;&lt;code&gt;main.rs&lt;/code&gt;&lt;/a&gt; to make it work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// main.rs&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;'static&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"Hello, world"&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;hello&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;tonic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;include_proto!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&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="nf"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;hello_service_client&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HelloServiceClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;HelloRequest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HelloServiceClient&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"http://grpcbin.test.k6.io:9000"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;tonic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;HelloRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;greeting&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"world"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="nf"&gt;.say_hello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="nf"&gt;.into_inner&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.reply&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[tokio::main]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&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="nf"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(())&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[cfg(test)]&lt;/span&gt;
&lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;#[test]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test_hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"Hello, world"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;#[tokio::test]&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;test_hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;assert_eq!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hello_world&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"hello world"&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;h3&gt;
  
  
  Step 5: &lt;a href="https://gitlab.com/yaghmaie/development-container/-/commit/04768d1ac3b718be6d0a215bf160b16864d98f1e"&gt;Cache installed dependencies &amp;amp; compilations&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;With this setup, &lt;code&gt;cargo&lt;/code&gt; has to download all the 3-rd party dependencies and compile them each time we run our pipeline. This is because the runs are isolated and cannot interfere with each other. But unlike code, the dependencies not often change. Therefore, it would be a great win if we could reuse that effort.&lt;/p&gt;

&lt;p&gt;We can easily achieve it by utilizing &lt;a href="https://docs.gitlab.com/ee/ci/caching/"&gt;Gitlab CI's cache&lt;/a&gt;. &lt;a href="https://doc.rust-lang.org/cargo/index.html"&gt;&lt;strong&gt;The Cargo Book&lt;/strong&gt;&lt;/a&gt; lists the &lt;a href="https://doc.rust-lang.org/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci"&gt;files and directories&lt;/a&gt; we can cache to get the most efficient experience while installing dependencies. There is also a &lt;a href="https://doc.rust-lang.org/cargo/guide/build-cache.html"&gt;build cache&lt;/a&gt; (&lt;code&gt;/target&lt;/code&gt; directory) which is output of a build and we can safely store it in our CI cache to speed up our compilation time.&lt;/p&gt;

&lt;p&gt;There is a limitation with caching in Gitlab CI which is the paths we define to be cached must be within the project directory. It means we cannot save the files in &lt;a href="https://doc.rust-lang.org/cargo/guide/cargo-home.html"&gt;&lt;strong&gt;Cargo Home&lt;/strong&gt;&lt;/a&gt; unless we change it's path to somewhere within our project's directory. Thus, we need to set the &lt;code&gt;CARGO_HOME&lt;/code&gt; environment variable to something like &lt;code&gt;${CI_PROJECT_DIR}/.cargo&lt;/code&gt;. If you are wondering what &lt;code&gt;${CI_PROJECT_DIR}&lt;/code&gt; is, then you need to take a look at &lt;a href="https://docs.gitlab.com/ee/ci/variables/predefined_variables.html"&gt;Gitlab CI's predefined variables&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Finally we end up here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .gitlab-ci.yml&lt;/span&gt;

&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CARGO_HOME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${CI_PROJECT_DIR}/.cargo&lt;/span&gt;

&lt;span class="s"&gt;cargo:test:&lt;/span&gt;
  &lt;span class="s"&gt;stage&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rust:alpine3.18&lt;/span&gt;
  &lt;span class="s"&gt;cache&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull-push&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/.crates.toml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/.crates2.json&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/bin/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/registry/index/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/registry/cache/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/git/db/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/bin&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;target/&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on_success&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apk add musl-dev&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;apk add protoc&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cargo test&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the first run after checking in these changes it got &lt;a href="https://gitlab.com/yaghmaie/development-container/-/pipelines/1044765415"&gt;longer than 2 minutes&lt;/a&gt; for the pipeline to finish. But, the second run took &lt;a href="https://gitlab.com/yaghmaie/development-container/-/pipelines/1044768619"&gt;52 seconds&lt;/a&gt; which indicates a build with cache is at least twice faster in this case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6: &lt;a href="https://gitlab.com/yaghmaie/development-container/-/commit/7a4f81f0d0de2401ed18626da30e24b11d3cdd32"&gt;Make development container&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;At this point we have a working feature and we improved caching in our pipeline. But right now, whenever we want to run &lt;code&gt;cargo&lt;/code&gt; we have to install OS packages (&lt;code&gt;musl-dev&lt;/code&gt; &amp;amp; &lt;code&gt;protoc&lt;/code&gt;) first. This is far from ideal since there should be no need to install them each time a job in the pipeline uses &lt;code&gt;cargo&lt;/code&gt;. It is not possible to use cache in this case because the installed files are outside project directory and there is no good way to bring them in.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Development Container&lt;/strong&gt; can solve this problem for us. Instead of doing our operations in the official rust container, we can make a new image based on it that suits our specific needs.&lt;/p&gt;

&lt;p&gt;First thing we need is a &lt;em&gt;Dockerfile&lt;/em&gt;. So, we move those two lines out of the test stage and bring them into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Development.Dockerfile&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; rust:alpine3.18&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; musl-dev&lt;span class="o"&gt;=&lt;/span&gt;1.2.4-r2
&lt;span class="k"&gt;RUN &lt;/span&gt;apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="nv"&gt;protoc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3.21.12-r2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More specific package versions are encouraged for more predictable outcomes.&lt;/p&gt;

&lt;p&gt;We will build an image from this file and push it to our project's &lt;a href="https://docs.gitlab.com/ee/user/packages/container_registry/"&gt;Container Registry&lt;/a&gt; at the first stage (&lt;em&gt;prepare&lt;/em&gt;) of the pipeline and will use it in the rest of the stages when needed.&lt;/p&gt;

&lt;p&gt;You might ask if building a container on every run is wasteful or not? Of course it could be. But, using &lt;code&gt;docker build&lt;/code&gt; we can &lt;a href="https://docs.docker.com/engine/reference/commandline/build/#cache-from"&gt;specify external cache sources&lt;/a&gt; with &lt;code&gt;--cache-from&lt;/code&gt; that makes the process really efficient. Containers' layered architecture makes not just building but also storing them very efficient. You can learn more about how it works &lt;a href="https://docs.docker.com/build/cache/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We also need to decide on naming and tagging our development containers. Fortunately, Gitlab CI has a variable for the registry image (&lt;code&gt;$CI_REGISTRY_IMAGE&lt;/code&gt;) we can use and define new variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# .gitlab-ci.yml&lt;/span&gt;

&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;DEVELOPMENT_IMAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE/development&lt;/span&gt;
  &lt;span class="na"&gt;DEVELOPMENT_IMAGE_MAIN_BRANCH_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DEVELOPMENT_IMAGE:main&lt;/span&gt;
  &lt;span class="na"&gt;DEVELOPMENT_IMAGE_CURRENT_BRANCH_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DEVELOPMENT_IMAGE:$CI_COMMIT_BRANCH&lt;/span&gt;
  &lt;span class="na"&gt;DEVELOPMENT_IMAGE_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DEVELOPMENT_IMAGE:$CI_COMMIT_SHORT_SHA&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For you to have a better understanding, in my case these can translate into the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DEVELOPMENT_IMAGE: registry.gitlab.com/yaghmaie/development-container/development&lt;/li&gt;
&lt;li&gt;DEVELOPMENT_IMAGE_MAIN_BRANCH_TAG: registry.gitlab.com/yaghmaie/development-container/development:main&lt;/li&gt;
&lt;li&gt;DEVELOPMENT_IMAGE_CURRENT_BRANCH_TAG: registry.gitlab.com/yaghmaie/development-container/development:some-other-branch&lt;/li&gt;
&lt;li&gt;DEVELOPMENT_IMAGE_TAG: registry.gitlab.com/yaghmaie/development-container/development:7a4f81f0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We want to use &lt;code&gt;DEVELOPMENT_IMAGE_MAIN_BRANCH_TAG&lt;/code&gt; and &lt;code&gt;DEVELOPMENT_IMAGE_CURRENT_BRANCH_TAG&lt;/code&gt; as cache sources to build &lt;code&gt;DEVELOPMENT_IMAGE_TAG&lt;/code&gt; and update &lt;code&gt;DEVELOPMENT_IMAGE_CURRENT_BRANCH_TAG&lt;/code&gt;. We will use &lt;code&gt;DEVELOPMENT_IMAGE_TAG&lt;/code&gt; in the rest of the stages of our pipeline.&lt;/p&gt;

&lt;p&gt;This is how to build the image the way described:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cache-from&lt;/span&gt; &lt;span class="nv"&gt;$DEVELOPMENT_IMAGE_MAIN_BRANCH_TAG&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cache-from&lt;/span&gt; &lt;span class="nv"&gt;$DEVELOPMENT_IMAGE_CURRENT_BRANCH_TAG&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;BUILDKIT_INLINE_CACHE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--file&lt;/span&gt; Development.Dockerfile &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag&lt;/span&gt; &lt;span class="nv"&gt;$DEVELOPMENT_IMAGE_TAG&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tag&lt;/span&gt; &lt;span class="nv"&gt;$DEVELOPMENT_IMAGE_CURRENT_BRANCH_TAG&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By setting &lt;code&gt;BUILDKIT_INLINE_CACHE&lt;/code&gt; build argument, we write cache metadata in the container so it can be used as cache source for later builds.&lt;/p&gt;

&lt;p&gt;After the build is done, it is pushed to the container registry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker push &lt;span class="nt"&gt;--all-tags&lt;/span&gt; &lt;span class="nv"&gt;$DEVELOPMENT_IMAGE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; will be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;stages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;prepare&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;

&lt;span class="na"&gt;variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;CARGO_HOME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${CI_PROJECT_DIR}/.cargo&lt;/span&gt;
  &lt;span class="na"&gt;DEVELOPMENT_IMAGE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$CI_REGISTRY_IMAGE/development&lt;/span&gt;
  &lt;span class="na"&gt;DEVELOPMENT_IMAGE_MAIN_BRANCH_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DEVELOPMENT_IMAGE:main&lt;/span&gt;
  &lt;span class="na"&gt;DEVELOPMENT_IMAGE_CURRENT_BRANCH_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DEVELOPMENT_IMAGE:$CI_COMMIT_BRANCH&lt;/span&gt;
  &lt;span class="na"&gt;DEVELOPMENT_IMAGE_TAG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DEVELOPMENT_IMAGE:$CI_COMMIT_SHORT_SHA&lt;/span&gt;

&lt;span class="s"&gt;docker:build:development:&lt;/span&gt;
  &lt;span class="s"&gt;stage&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prepare&lt;/span&gt;
  &lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker:24&lt;/span&gt;
  &lt;span class="s"&gt;services&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker:24-dind&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;docker build \&lt;/span&gt;
        &lt;span class="s"&gt;--cache-from $DEVELOPMENT_IMAGE_MAIN_BRANCH_TAG \&lt;/span&gt;
        &lt;span class="s"&gt;--cache-from $DEVELOPMENT_IMAGE_CURRENT_BRANCH_TAG \&lt;/span&gt;
        &lt;span class="s"&gt;--build-arg BUILDKIT_INLINE_CACHE=1 \&lt;/span&gt;
        &lt;span class="s"&gt;--file Development.Dockerfile \&lt;/span&gt;
        &lt;span class="s"&gt;--tag $DEVELOPMENT_IMAGE_TAG \&lt;/span&gt;
        &lt;span class="s"&gt;--tag $DEVELOPMENT_IMAGE_CURRENT_BRANCH_TAG \&lt;/span&gt;
        &lt;span class="s"&gt;.&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;docker push --all-tags $DEVELOPMENT_IMAGE&lt;/span&gt;

&lt;span class="s"&gt;cargo:test:&lt;/span&gt;
  &lt;span class="s"&gt;stage&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test&lt;/span&gt;
  &lt;span class="s"&gt;image&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;$DEVELOPMENT_IMAGE_TAG&lt;/span&gt;
  &lt;span class="s"&gt;cache&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pull-push&lt;/span&gt;
    &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/.crates.toml&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/.crates2.json&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/bin/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/registry/index/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/registry/cache/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/git/db/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;.cargo/bin&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;target/&lt;/span&gt;
    &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on_success&lt;/span&gt;
  &lt;span class="na"&gt;script&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;cargo test&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want more details about this last change, please go ahead and take a look at &lt;a href="https://gitlab.com/yaghmaie/development-container/-/commit/7a4f81f0d0de2401ed18626da30e24b11d3cdd32"&gt;it's commit&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Despite all the advancement with development tools, keeping a CI pipeline fast while reliable can be challenging. Working on a CI pipeline without tests and builds is sort of meaningless. So, in this post we started with a "hello, world" rust project and progressed into calling a gRPC API, then making development container for builds and tests in the CI. All the files and related date for this exercise can be found at &lt;a href="https://gitlab.com/yaghmaie/development-container"&gt;&lt;strong&gt;Development Container repository&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>gitlab</category>
      <category>container</category>
      <category>pipeline</category>
    </item>
    <item>
      <title>Skip Polkit prompt when authorizing SSH key access with 1Password</title>
      <dc:creator>Pejman Yaghmaie</dc:creator>
      <pubDate>Tue, 10 Oct 2023 10:54:18 +0000</pubDate>
      <link>https://dev.to/yaghmaie/skip-polkit-prompt-when-authorizing-ssh-key-access-with-1password-626</link>
      <guid>https://dev.to/yaghmaie/skip-polkit-prompt-when-authorizing-ssh-key-access-with-1password-626</guid>
      <description>&lt;p&gt;If you use &lt;a href="https://developer.1password.com/docs/ssh/agent/"&gt;1Password's SSH Agent&lt;/a&gt; to manage access to a lot of SSH keys on GNU/Linux, then you probably go through this process multiple times a day:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Connecting to a host via SSH (when using &lt;code&gt;ssh&lt;/code&gt; command, or &lt;code&gt;git&lt;/code&gt;, etc.):
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--aBPu9-qQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/yaghmaie/Essence/main/posts/1password-skip-polkit-prompt/ssh.png" alt="connecting to some host via ssh" title="Connecting to some host via ssh" width="376" height="225"&gt;
&lt;/li&gt;
&lt;li&gt;1Password asking to authorize access:
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ga-b8pQX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/yaghmaie/Essence/main/posts/1password-skip-polkit-prompt/1password-dialog.png" alt="1password ask for authorization" title="1Password ask for authorization" width="496" height="460"&gt;
&lt;/li&gt;
&lt;li&gt;Polkit prompting to unlock 1Password (&lt;a href="https://support.1password.com/system-authentication-linux/"&gt;System authentication&lt;/a&gt;):
&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--d4RNv8WA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://raw.githubusercontent.com/yaghmaie/Essence/main/posts/1password-skip-polkit-prompt/polkit-prompt.png" alt="Polkit prompt" title="Entering system's password upon Polkit's request" width="710" height="359"&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Visiting two different dialogs followed by entering a &lt;code&gt;l*************ng&lt;/code&gt; password may not be the most fun experience you can get. So, how to make it better?&lt;/p&gt;

&lt;h2&gt;
  
  
  Skip Polkit prompt by adding a rule
&lt;/h2&gt;

&lt;p&gt;You can add a &lt;a href="https://www.freedesktop.org/software/polkit/docs/latest/polkit.8.html"&gt;Polkit authorization rule&lt;/a&gt; to unlock 1Password without being asked for authentication. A Polkit rule is basically a JavaScript function that overrules the default settings.&lt;/p&gt;

&lt;p&gt;These rules can be found in the following two places:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;/usr/share/polkit-1/rules.d&lt;/code&gt;: Used by packages to add their rules&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/etc/polkit-1/rules.d&lt;/code&gt;: Used for local configuration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, we'll create a new file at &lt;code&gt;/etc/polkit-1/rules.d/00-1password-noauth.rules&lt;/code&gt;. Then we add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;polkit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;addRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;)&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="p"&gt;(&lt;/span&gt;
            &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;com.1password.1Password.unlock&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
            &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;com.1password.1Password.authorizeSshAgent&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
        &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isInGroup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wheel&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;polkit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;YES&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;There is no need to reboot system or restart any service after saving the rule file. Polkit will detect and apply changes immediately.&lt;/p&gt;

&lt;p&gt;Here we check if the &lt;code&gt;action&lt;/code&gt;'s &lt;code&gt;id&lt;/code&gt; match the ones defined by 1Password. You can find these &lt;code&gt;id&lt;/code&gt;s at &lt;code&gt;/usr/share/polkit-1/actions/com.1password.1Password.policy&lt;/code&gt; installed by 1Password for use with system authentication. We also check if the user is in &lt;a href="https://en.wikipedia.org/wiki/Wheel_(computing)"&gt;wheel group&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You might ask why we name our rule file &lt;code&gt;00-1password-noauth.rules&lt;/code&gt; and not &lt;code&gt;1password-noauth.rules&lt;/code&gt;? Because the rules are read and processed by Polkit in a certain order and this is how it's determined. Rules with smaller numbers are processed first.&lt;/p&gt;

&lt;p&gt;1Password makes it much easier and safer to keep and manage SSH keys. However, with some Linux setups such as mine, you'd have to enter system's password multiple times a day which is very annoying... and meaningless since the vault and the systems is already unlocked!&lt;/p&gt;

</description>
      <category>1password</category>
      <category>polkit</category>
      <category>sshagent</category>
      <category>authorization</category>
    </item>
  </channel>
</rss>
