<?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: Jack Flannery</title>
    <description>The latest articles on DEV Community by Jack Flannery (@jackf).</description>
    <link>https://dev.to/jackf</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%2F324083%2F5dfec7ea-ef73-41ae-bdbf-e3ab0ac311fa.jpeg</url>
      <title>DEV Community: Jack Flannery</title>
      <link>https://dev.to/jackf</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jackf"/>
    <language>en</language>
    <item>
      <title>Test Driving a Rails API - Part Two</title>
      <dc:creator>Jack Flannery</dc:creator>
      <pubDate>Fri, 22 Mar 2024 01:28:25 +0000</pubDate>
      <link>https://dev.to/jackf/test-driving-a-rails-api-part-two-1ec7</link>
      <guid>https://dev.to/jackf/test-driving-a-rails-api-part-two-1ec7</guid>
      <description>&lt;h2&gt;
  
  
  Part 2 - Setup the test environment with minitest/spec and rack-test
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Recap
&lt;/h3&gt;

&lt;p&gt;This is the second part of my Test Driving a Rails API series. In &lt;a href="https://dev.to/jackf/test-driving-a-rails-api-part-one-4ej0"&gt;Part 1&lt;/a&gt; we set up our development environment, generated a &lt;a href="https://guides.rubyonrails.org/api_app.html"&gt;Rails API-only application&lt;/a&gt;, installed &lt;a href="https://github.com/bkeepers/dotenv"&gt;dotenv&lt;/a&gt; to easily store configuration values in the environment, and installed and configured &lt;a href="https://www.postgresql.org/docs/16/index.html"&gt;PostgreSQL version 16&lt;/a&gt; as our database.&lt;/p&gt;

&lt;p&gt;In this part, we’ll set up our testing environment so that we can test our Rails API using &lt;a href="https://github.com/minitest/minitest"&gt;minitest&lt;/a&gt; with &lt;a href="https://github.com/minitest/minitest?tab=readme-ov-file#specs-"&gt;minitest/spec&lt;/a&gt;. We’ll look at the differences between traditional style unit tests and spec-style tests, or specs. I’ll demonstrate why you should use &lt;a href="https://github.com/minitest/minitest-rails"&gt;minitest-rails&lt;/a&gt;. We’ll look at using &lt;a href="https://github.com/rack/rack-test"&gt;rack-test&lt;/a&gt; for testing our API. We’ll even create our own generator to generate API specs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing Rails applications
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://guides.rubyonrails.org/testing.html"&gt;Testing&lt;/a&gt; has always been an important part of the Rails ecosystem. Writing tests allows you to ensure your code works correctly and predictably under various conditions, reducing unexpected errors and bugs. Having a repeatable test suite allows you to be constantly making changes to your codebase while ensuring existing functionality continues to work as intended.&lt;/p&gt;

&lt;p&gt;We’ll practice &lt;a href="https://martinfowler.com/bliki/TestDrivenDevelopment.html"&gt;Test Driven Development (TDD)&lt;/a&gt; by writing the tests first, then implementing the code to make the tests pass, and then refactor the code, ensuring the test continues to pass.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minitest vs Rspec
&lt;/h3&gt;

&lt;p&gt;When starting a Rails project, you have a lot of decisions to make. Whether or not to write tests should not be one of them. The big decision is to use &lt;a href="https://github.com/minitest/minitest"&gt;Minitest&lt;/a&gt; or &lt;a href="https://rspec.info/"&gt;Rspec&lt;/a&gt;. Both of those testing frameworks are great and provide everything you need to test a Rails application thoroughly.&lt;/p&gt;

&lt;p&gt;Rspec has been around longer and provides a great &lt;a href="https://thoughtbot.com/blog/writing-a-domain-specific-language-in-ruby"&gt;DSL&lt;/a&gt; that allows you to write very readable tests, or specs as they are called when written in a spec-style with a DSL. Minitest is more lightweight and is now the standard Rails testing framework. I really appreciate Minitest’s simplicity, but I do like Rspec’s DSL and prefer spec-style tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  minitest/spec
&lt;/h3&gt;

&lt;p&gt;Fortunately, Minitest provides a DSL of its own in &lt;a href="https://github.com/minitest/minitest/blob/master/lib/minitest/spec.rb"&gt;minitest/spec&lt;/a&gt;. It offers the best of both worlds: Minitest's simplicity and the improved readability and writability of spec-style tests. When using Minitest, you have the choice of classic-style tests or spec-style tests with &lt;code&gt;minitest/spec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In this post, we’ll write specs with &lt;code&gt;minitest/spec&lt;/code&gt;. I mentioned that Minitest is now Rails's default testing framework, and &lt;code&gt;minitest/spec&lt;/code&gt; is part of Minitest. Technically, no extra gems are needed to use it in a new Rails project. However, I will explain later why you should also use the &lt;a href="https://github.com/minitest/minitest-rails/tree/master"&gt;minitest-rails&lt;/a&gt; gem for better integration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tests vs Specs
&lt;/h3&gt;

&lt;p&gt;Let's say you have an &lt;code&gt;Event&lt;/code&gt; ActiveRecord model. A model test, in traditional test style, uses normal Ruby class declaration syntax like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;require &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

class EventTest &amp;lt; ActiveSupport::TestCase
end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;EventTest&lt;/code&gt; is a test class and subclass of &lt;a href="https://api.rubyonrails.org/v7.1.2/classes/ActiveSupport/TestCase.html"&gt;ActiveSupport::TestCase&lt;/a&gt;, which is provided by Rails. &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; is a subclass of &lt;code&gt;Minitest::Test&lt;/code&gt;, a class provided by Minitest.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Any class with &lt;code&gt;Minitest::Test&lt;/code&gt; as an ancestor is a Minitest test class.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Therefore, &lt;code&gt;EventTest&lt;/code&gt; above is a test class.&lt;/p&gt;

&lt;p&gt;A spec-style test, or spec, of the same &lt;code&gt;Event&lt;/code&gt; model would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;require &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

describe Event &lt;span class="k"&gt;do
&lt;/span&gt;end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of a class declaration, you use a &lt;code&gt;describe&lt;/code&gt; block to contain the test class. &lt;code&gt;describe&lt;/code&gt; is just a method provided by &lt;code&gt;Minitest::Spec::DSL&lt;/code&gt; that we call, passing to it the &lt;code&gt;Event&lt;/code&gt; class and a block. You can actually pass any number of parameters of any type to &lt;code&gt;describe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Under the hood, an instance of an anonymous class is created with the class &lt;a href="https://github.com/minitest/minitest/blob/master/lib/minitest/spec.rb#L106C1-L106C38"&gt;Minitest::Spec&lt;/a&gt; as its superclass. &lt;code&gt;Minitest::Spec&lt;/code&gt; is a subclass of &lt;code&gt;Minitest::Test&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Just as in traditional style Minitest tests, a spec’s test class must be an ancestor of &lt;code&gt;Minitest::Test&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Even though you couldn’t guess by looking at the above spec, everything that happens inside the outermost &lt;code&gt;describe&lt;/code&gt; block is executed inside the context of an anonymous subclass of &lt;code&gt;Minitest::Spec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That means, unlike in the traditional style &lt;code&gt;Event&lt;/code&gt; test above, &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; will not be in the test class’s ancestor chain in the spec version.&lt;/p&gt;

&lt;p&gt;Without &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; you will lose all of it’s functionality including having your tests wrapped in database transactions. Test data will remain in the database between runs and will probably affect your test results.&lt;/p&gt;

&lt;h3&gt;
  
  
  ActiveSupport::TestCase
&lt;/h3&gt;

&lt;p&gt;Rails provides &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; as the base test class for all tests. Several subclasses of &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; have additional useful methods for testing different types of components, including &lt;a href="https://api.rubyonrails.org/v7.1.2/classes/ActionDispatch/IntegrationTest.html"&gt;ActionDispatch::IntegrationTest&lt;/a&gt; for Rails Controllers and integration tests and &lt;a href="https://api.rubyonrails.org/v7.1.2/classes/ActionView/TestCase.html"&gt;ActionView::TestCase&lt;/a&gt; for Rails Views.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; provides important functionality to your tests, including database transactions as mentioned earlier.&lt;/p&gt;

&lt;p&gt;In the above &lt;code&gt;Event&lt;/code&gt; spec, &lt;code&gt;Minitest::Spec&lt;/code&gt; is the test class, and &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; is nowhere in the ancestor chain.&lt;/p&gt;

&lt;p&gt;How do we tell &lt;code&gt;minitest/spec&lt;/code&gt; which test class to use? The tldr is to use the &lt;a href="https://github.com/minitest/minitest-rails"&gt;minitest-rails&lt;/a&gt; gem. I highly recommend adding the &lt;code&gt;minitest-rails&lt;/code&gt; gem to your project. The gem does a lot to integrate the Rails testing ecosystem with Minitest, particularly spec-style tests. One primary benefit is that it configures specs to use &lt;a href="https://api.rubyonrails.org/v7.1.2/classes/ActiveSupport/TestCase.html"&gt;ActiveSupport::TestCase&lt;/a&gt; or one of its subclasses as the spec’s test class, depending on what is passed &lt;code&gt;describe&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let’s look at how &lt;code&gt;minitest-rails&lt;/code&gt; achieves this. It takes advantage of the &lt;code&gt;register_spec_type&lt;/code&gt; method in the &lt;code&gt;Minitest::Spec::DSL&lt;/code&gt; module.&lt;/p&gt;

&lt;h3&gt;
  
  
  register_spec_type
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;Minitest::Spec::DSL&lt;/code&gt; module provides the &lt;code&gt;register_spec_type&lt;/code&gt; method for the purpose of deciding which class to use as a spec’s test class.&lt;/p&gt;

&lt;p&gt;There are two ways to call &lt;code&gt;register_spec_type&lt;/code&gt;. The first is by passing a class constant as the only parameter, and a block. When Minitest sees an outer &lt;code&gt;describe&lt;/code&gt; call, all parameters passed to &lt;code&gt;describe&lt;/code&gt; are passed to the block. The block is used to test the parameters and determine if the passed in class should be used as the test class for that spec. If the block returns a &lt;code&gt;truthy&lt;/code&gt; value, then the class passed as the first parameter will be used for that spec’s test class.&lt;/p&gt;

&lt;p&gt;To demonstrate how it works, consider again this spec for the &lt;code&gt;Event&lt;/code&gt; ActiveRecord model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;Event&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we pass the &lt;code&gt;Event&lt;/code&gt; class to &lt;code&gt;describe&lt;/code&gt;. &lt;code&gt;Event&lt;/code&gt; is a subclass of &lt;code&gt;ActiveRecord::Base&lt;/code&gt;. We want to tell &lt;code&gt;minitest/spec&lt;/code&gt; to use &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; whenever an &lt;code&gt;ActiveRecord::Base&lt;/code&gt; class is passed to &lt;code&gt;describe&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To achieve this with &lt;code&gt;register_spec_type&lt;/code&gt;, it would look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;register_spec_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the spec, the &lt;code&gt;Event&lt;/code&gt; model class is the only argument passed to &lt;code&gt;describe&lt;/code&gt; and thus will be the value of the &lt;code&gt;desc&lt;/code&gt; block parameter. It returns true if &lt;code&gt;desc&lt;/code&gt; is a subclass of &lt;code&gt;ActiveRecord::Base&lt;/code&gt;. In the case of &lt;code&gt;Event&lt;/code&gt;, the block will return true, making &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; the spec’s test class.&lt;/p&gt;

&lt;p&gt;Another strategy to call &lt;code&gt;register_spec_type&lt;/code&gt; is like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;register_spec_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;addl&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;addl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt; &lt;span class="ss"&gt;:model&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we disregard the first parameter and check for a second parameter. &lt;code&gt;_desc&lt;/code&gt; still represents the first parameter passed to &lt;code&gt;describe&lt;/code&gt;; by convention, it’s prefixed with a &lt;code&gt;_&lt;/code&gt; because we won’t be using it. The &lt;code&gt;addl&lt;/code&gt; parameter, thanks to Ruby’s splat operator &lt;code&gt;*&lt;/code&gt;, is an array of the remaining arguments passed to &lt;code&gt;describe&lt;/code&gt;. If the symbol &lt;code&gt;:model&lt;/code&gt; was passed as an argument, the block will return true, again making &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; the spec’s test class.&lt;/p&gt;

&lt;p&gt;That allows us to structure our model spec like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'Event'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:model&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second form of &lt;code&gt;register_spec_type&lt;/code&gt; accepts a &lt;code&gt;Regexp&lt;/code&gt; as the first argument and a class as the second. If the &lt;code&gt;Regexp&lt;/code&gt; matches the first argument to &lt;code&gt;describe&lt;/code&gt;, then the class passed as the second argument will be the spec’s test class. We’ll use this second form later on.&lt;/p&gt;

&lt;p&gt;This demonstrates how &lt;code&gt;register_spec_type&lt;/code&gt; gives you complete control over a spec’s test class.&lt;/p&gt;

&lt;h3&gt;
  
  
  minitest-rails
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;minitest-rails&lt;/code&gt; does many things for you to improve the experience of using Minitest with Rails. One of those things is taking care of all of the &lt;code&gt;register_spec_type&lt;/code&gt; calls such that each type of Rails component that you test will use the intended test class.&lt;/p&gt;

&lt;p&gt;To give you an idea of how you might implement this on your own, consider the following code. This is inspired by the code in &lt;a href="https://github.com/minitest/minitest-rails/blob/master/lib/minitest/rails.rb"&gt;minitest-rails/lib/minitest/rails.rb&lt;/a&gt;, but this is what it would look like in your own &lt;code&gt;test_helper.rb&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/test_helper.rb&lt;/span&gt;

&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"RAILS_ENV"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s2"&gt;"../config/environment"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rails/test_help"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'minitest/autorun'&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ActiveSupport&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt;
    &lt;span class="c1"&gt;# Run tests in parallel with specified workers&lt;/span&gt;
    &lt;span class="n"&gt;parallelize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;workers: :number_of_processors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kp"&gt;extend&lt;/span&gt; &lt;span class="no"&gt;Minitest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Spec&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;DSL&lt;/span&gt;

    &lt;span class="n"&gt;register_spec_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="n"&gt;register_spec_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;addl&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;addl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt; &lt;span class="ss"&gt;:model&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ActionDispatch::IntegrationTest&lt;/span&gt;
  &lt;span class="n"&gt;register_spec_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;desc&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActionController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Metal&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;desc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;is_a?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;register_spec_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;addl&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;addl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt; &lt;span class="ss"&gt;:integration&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; must be extended with &lt;code&gt;Minitest::Spec::DSL&lt;/code&gt; before it can use &lt;code&gt;register_spec_type&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is what &lt;code&gt;minitest-rails&lt;/code&gt; does for you, but what you see above only scratches the surface. This code above only takes care of model, controller, and integration specs; there are other types of specs to consider as well. And &lt;code&gt;register_spec_type&lt;/code&gt; is not the only thing &lt;code&gt;minitest-rails&lt;/code&gt; does for you.&lt;/p&gt;

&lt;p&gt;To install it, let’s add the gem to the &lt;code&gt;development&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; group of our &lt;code&gt;Gemfile&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;  &lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:development&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="c1"&gt;# (other gems)&lt;/span&gt;

    &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"minitest-rails"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 7.1.0"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And install the gem:&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;bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the &lt;code&gt;minitest-rails&lt;/code&gt; install task, which will generate a new &lt;code&gt;test/test_helper.rb&lt;/code&gt; file. Run this task from the app’s root with the argument &lt;code&gt;.&lt;/code&gt;, representing the current directory:&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;rails generate minitest:install &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We already have the default &lt;code&gt;test/test_helper.rb&lt;/code&gt; file created by Rails, so there is a conflict. When asked if you want to overwrite it, just say yes &lt;code&gt;Y&lt;/code&gt;, since we haven’t modified or added anything important to the file yet. If you already have code in your test_helper.rb file that you don’t want to lose, make a backup and then merge it with the newly generated one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Overwrite /Users/jack/projects/public/my_api/test/test_helper.rb? &lt;span class="o"&gt;(&lt;/span&gt;enter &lt;span class="s2"&gt;"h"&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;Ynaqdhm] Y
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The install task will also attempt to generate several test directories. Most already exist, but you might wind up with new &lt;code&gt;test/helpers&lt;/code&gt; and &lt;code&gt;test/fixtures&lt;/code&gt; directories. You can delete those, but if you want to commit the empty directories to your git repository, commit the &lt;code&gt;.keep&lt;/code&gt; files inside.&lt;/p&gt;

&lt;p&gt;We now have a new line in our &lt;code&gt;test/test_helper.rb&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"minitest/rails"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I highly recommend reviewing &lt;a href="https://github.com/minitest/minitest-rails/blob/master/lib/minitest/rails.rb"&gt;the code in that file&lt;/a&gt;. Among other things, you’ll see many different calls to &lt;code&gt;register_spec_type&lt;/code&gt;. Thankfully, we don’t have to write and maintain all of those.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing APIs with rack-test
&lt;/h3&gt;

&lt;p&gt;As mentioned earlier, Rails provides &lt;code&gt;ActionDispatch::IntegrationTest&lt;/code&gt; for controller and integration tests. While it does give you some handy methods for making HTTP requests and testing your services, I prefer to use &lt;a href="https://github.com/rack/rack-test"&gt;rack-test&lt;/a&gt; for testing APIs. It provides better support for things like setting headers and request cookies and maintains a &lt;a href="https://github.com/rack/rack-test/blob/main/lib/rack/test/cookie_jar.rb"&gt;Cookie Jar&lt;/a&gt; between requests.&lt;/p&gt;

&lt;p&gt;To install &lt;code&gt;rack-test&lt;/code&gt;, add it to the &lt;code&gt;test&lt;/code&gt; group in your Gemfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"rack-test"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And install the gem:&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;bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To integrate &lt;code&gt;rack-test&lt;/code&gt;, let's create our own custom test class for API specs. In &lt;code&gt;test/test_helper.rb&lt;/code&gt; after the &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; reopening, add this &lt;code&gt;ApiIntegrationTestCase&lt;/code&gt; class definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# test/test_helper.rb&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiIntegrationTestCase&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Methods&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;
    &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'config.ru'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll get all of the behavior of &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; plus everything we need to use &lt;code&gt;rack-test&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;While we’re at it, let's add a couple of other gems we’ll need for our test environment: &lt;a href="https://github.com/thoughtbot/factory_bot_rails"&gt;factory_bot_rails&lt;/a&gt; is a fixtures replacement and generates test model instances. &lt;a href="https://github.com/faker-ruby/faker"&gt;faker&lt;/a&gt; is handy for generating fake strings of data to be used in tests. Add those gems to the &lt;code&gt;development&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; group of your Gemfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:development&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# (other gems)&lt;/span&gt;

  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"factory_bot_rails"&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s2"&gt;"faker"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And install the gems:&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;bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To finish adding &lt;code&gt;factory_bot_rails&lt;/code&gt; to the project, go back into &lt;code&gt;test_helper.rb&lt;/code&gt;. We want the convenient &lt;code&gt;factory_bot&lt;/code&gt; methods to be available in all types of tests, so add this line into the &lt;code&gt;ActiveSupport::TestCase&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;FactoryBot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Syntax&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Methods&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While you’re there, you can remove the fixtures line, unless you plan to use fixtures, which is fine, but I won’t be using them in this series, in favor of factories.&lt;/p&gt;

&lt;p&gt;We need to do one more thing: configure &lt;code&gt;minitest/spec&lt;/code&gt; to use our new &lt;code&gt;ApiIntegrationTestCase&lt;/code&gt; test class for API specs. For that, we’ll reach for our old friend &lt;code&gt;register_spec_type&lt;/code&gt;, who we know very well at this point.&lt;/p&gt;

&lt;p&gt;First, let's consider what an API spec might look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'test_helper'&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'Events API'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'EventsApi'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'Events'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want specs in all the above forms to use &lt;code&gt;ApiIntegrationTestCase&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As you can see above, when testing an API, we’ll pass a string to &lt;code&gt;describe&lt;/code&gt;. In that case, we can use the second form of &lt;code&gt;register_spec_type&lt;/code&gt;, which accepts a &lt;code&gt;Regexp&lt;/code&gt; as the first parameter.&lt;/p&gt;

&lt;p&gt;We can add this call to &lt;code&gt;register_spec_type&lt;/code&gt; inside the &lt;code&gt;ApiIntegrationTestCase&lt;/code&gt; class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;register_spec_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\w+\s?API$/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will match a string, followed by one optional space, then the string “API”, all case insensitive. &lt;code&gt;self&lt;/code&gt; here will be &lt;code&gt;ApiIntegrationTestCase&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For good measure, let's support the alternate way of declaring an API spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;register_spec_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;addl&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;addl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This allows for this type of spec:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s1"&gt;'Events'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your complete &lt;code&gt;test/test_helper.rb&lt;/code&gt; file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"RAILS_ENV"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||=&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"MT_NO_EXPECTATIONS"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"true"&lt;/span&gt;

&lt;span class="nb"&gt;require_relative&lt;/span&gt; &lt;span class="s2"&gt;"../config/environment"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"rails/test_help"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"minitest/rails"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"minitest/pride"&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;ActiveSupport&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TestCase&lt;/span&gt;
    &lt;span class="c1"&gt;# Run tests in parallel with specified workers&lt;/span&gt;
    &lt;span class="n"&gt;parallelize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;workers: :number_of_processors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;FactoryBot&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Syntax&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Methods&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiIntegrationTestCase&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TestCase&lt;/span&gt;
  &lt;span class="kp"&gt;include&lt;/span&gt; &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Test&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Methods&lt;/span&gt;

  &lt;span class="n"&gt;register_spec_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/\w+\s?API$/i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;register_spec_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_desc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;addl&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;addl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;
    &lt;span class="no"&gt;Rack&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'config.ru'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like to follow the recommendation and set &lt;code&gt;ENV["MT_NO_EXPECTATIONS"] = "true"&lt;/code&gt;. We’ll talk more about that later.&lt;/p&gt;

&lt;p&gt;Be sure to require &lt;code&gt;minitest/pride&lt;/code&gt; because everyone deserves Fabulous tests!&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an Events API spec
&lt;/h3&gt;

&lt;p&gt;Another benefit of &lt;code&gt;minitest-rails&lt;/code&gt; is support for generating spec-style tests. With &lt;code&gt;minitest-rails&lt;/code&gt; installed as described above, any test generated will use the spec style by default.&lt;/p&gt;

&lt;p&gt;Since we’re practicing TDD, let’s generate an integration test for the Events API, even before creating the &lt;code&gt;Event&lt;/code&gt; model, with this command:&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;rails g integration_test events_api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That will generate the file &lt;code&gt;test/integration/events_api_test.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"Events api"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:integration&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="c1"&gt;# it "does a thing" do&lt;/span&gt;
  &lt;span class="c1"&gt;#   value(1+1).must_equal 2&lt;/span&gt;
  &lt;span class="c1"&gt;# end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is an integration spec. Modify it so that it's an API spec that uses &lt;code&gt;ApiIntegrationTestCase&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"Events API"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The string &lt;code&gt;"Events API"&lt;/code&gt; matches the &lt;code&gt;Regex&lt;/code&gt; &lt;code&gt;/\w+\s?API$/i&lt;/code&gt; that we used in our &lt;code&gt;register_spec_type&lt;/code&gt; call, so the &lt;code&gt;ApiIntegrationTestCase&lt;/code&gt; test class will be used here.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create an API spec generator
&lt;/h3&gt;

&lt;p&gt;This is good, but it would be better if we could generate an API spec directly, without having to make those modifications. Let's create a custom generator, that generates an API spec. I highly recommend reading through the &lt;a href="https://guides.rubyonrails.org/generators.html"&gt;Rails Guides on Generators&lt;/a&gt; to get a better understanding.&lt;/p&gt;

&lt;p&gt;Where do we begin when writing a custom generator? The answer is to use the generator generator, of course. Use this command to generate the skeleton of an &lt;code&gt;api_spec&lt;/code&gt; generator.&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;rails g generator api_spec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see in the output that some files and directories were created:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    create  lib/generators/api_spec
    create  lib/generators/api_spec/api_spec_generator.rb
    create  lib/generators/api_spec/USAGE
    create  lib/generators/api_spec/templates
    invoke  minitest
    create    &lt;span class="nb"&gt;test&lt;/span&gt;/lib/generators/api_spec_generator_test.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One thing that was generated was a test for our new generator. Open the generated test file &lt;code&gt;test/lib/generators/api_spec_generator_test.rb&lt;/code&gt; and replace the commented-out test with this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'generates an API spec'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;run_generator&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"nifty_things"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="n"&gt;assert_file&lt;/span&gt; &lt;span class="s2"&gt;"test/integration/nifty_things_api_spec.rb"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;assert_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/describe "NiftyThings API" do/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test file should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;
&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"generators/api_spec/api_spec_generator"&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="no"&gt;ApiSpecGenerator&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;tests&lt;/span&gt; &lt;span class="no"&gt;ApiSpecGenerator&lt;/span&gt;
  &lt;span class="n"&gt;destination&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"tmp/generators"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;setup&lt;/span&gt; &lt;span class="ss"&gt;:prepare_destination&lt;/span&gt;

  &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="s1"&gt;'generates an API spec'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;run_generator&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"nifty_things"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="n"&gt;assert_file&lt;/span&gt; &lt;span class="s2"&gt;"test/integration/nifty_things_api_test.rb"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;assert_match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/describe "NiftyThings API" do/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thanks to &lt;code&gt;minitest-rails&lt;/code&gt; and &lt;code&gt;register_spec_type&lt;/code&gt;, the test class will be &lt;code&gt;Rails::Generators::TestCase&lt;/code&gt;. You don’t need to pass &lt;code&gt;:generator&lt;/code&gt; as a second argument to &lt;code&gt;describe&lt;/code&gt; because &lt;code&gt;ApiSpecGenerator&lt;/code&gt; is a subclass of &lt;code&gt;Rails::Generators::Base&lt;/code&gt;. You can see how that works &lt;a href="https://github.com/minitest/minitest-rails/blob/master/lib/minitest/rails.rb#L98"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Execute the test with the following command:&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;rails &lt;span class="nb"&gt;test test&lt;/span&gt;/lib/generators/api_spec_generator_test.rb
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should give us the failure that we expect:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Failure:
ApiSpecGenerator::generator#test_0001_generates an API spec &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;/lib/generators/api_spec_generator_test.rb:12]:
Expected file &lt;span class="s2"&gt;"test/integration/nifty_things_api_test.rb"&lt;/span&gt; to exist, but does not
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To implement our generator and make the test pass, open &lt;code&gt;lib/generators/api_spec/api_spec_generator.rb&lt;/code&gt; and replace the contents of the file with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ApiSpecGenerator&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Generators&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;NamedBase&lt;/span&gt;
  &lt;span class="n"&gt;source_root&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expand_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"templates"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;__dir__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_api_spec_file&lt;/span&gt;
    &lt;span class="n"&gt;template&lt;/span&gt; &lt;span class="s1"&gt;'api_spec.rb.erb'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"test/integration/&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;file_name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_api_test.rb"&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This expects an &lt;code&gt;ERB&lt;/code&gt; template to exist. Create the file: &lt;code&gt;lib/generators/api_spec/templates/api_spec.rb.erb&lt;/code&gt; and add the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

&lt;span class="n"&gt;describe&lt;/span&gt; &lt;span class="s2"&gt;"&amp;lt;%= class_name %&amp;gt; API"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With those files in place, the tests should now be passing.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;ApiSpecGenerator&lt;/code&gt;, we used the variable &lt;code&gt;file_name&lt;/code&gt;, and in the template, we used the variable &lt;code&gt;class_name&lt;/code&gt;. That is thanks to using &lt;code&gt;Rails::Generators::NamedBase&lt;/code&gt; as our generator’s superclass.&lt;/p&gt;

&lt;p&gt;If you invoke the generator and give &lt;code&gt;“calendar_items”&lt;/code&gt; as the name argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails g api_spec calendar_items
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generator class will have access to the following variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;class_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"CalendarItems"&lt;/span&gt;
&lt;span class="n"&gt;file_name&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"calendar_items"&lt;/span&gt;
&lt;span class="n"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"calendar_items"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, update the generated &lt;code&gt;USAGE&lt;/code&gt; file &lt;code&gt;lib/generators/api_spec/USAGE&lt;/code&gt; with the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Description:
    Generates an API spec

Example:
    bin/rails generate api_spec nifty_things

    This will create:
        &lt;span class="nb"&gt;test&lt;/span&gt;/integration/nifty_thing_api_spec.rb

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

&lt;/div&gt;



&lt;p&gt;Our new generator now has decent help documentation:&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;rails g api_spec &lt;span class="nt"&gt;--help&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the Event API spec with the api_spec generator
&lt;/h3&gt;

&lt;p&gt;Now that we have our new generator, let's use it. Delete the Event API spec we generated previously:&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;&lt;span class="nb"&gt;rm test&lt;/span&gt;/integration/events_api_test.rb 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Invoke our custom generator:&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;rails g api_spec events
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That gives us our new &lt;code&gt;test/integration/events_api_test.rb&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;require &lt;span class="s2"&gt;"test_helper"&lt;/span&gt;

describe &lt;span class="s2"&gt;"Events API"&lt;/span&gt; &lt;span class="k"&gt;do
&lt;/span&gt;end
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now have a good starting point for testing the Events API.&lt;/p&gt;

&lt;p&gt;This is a good time to wrap up for now. In the next part, we’ll finally get into building the Events API. Our testing environment is all set up to start using TDD to drive our API’s development.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>minitest</category>
      <category>ruby</category>
      <category>testing</category>
    </item>
    <item>
      <title>Test Driving a Rails API - Part One</title>
      <dc:creator>Jack Flannery</dc:creator>
      <pubDate>Tue, 12 Mar 2024 03:32:25 +0000</pubDate>
      <link>https://dev.to/jackf/test-driving-a-rails-api-part-one-4ej0</link>
      <guid>https://dev.to/jackf/test-driving-a-rails-api-part-one-4ej0</guid>
      <description>&lt;h2&gt;
  
  
  Part 1: Setup the Environment and Create a Rails Application
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Intro to REST APIs
&lt;/h3&gt;

&lt;p&gt;In this tutorial, I will show you how to create an &lt;a href="https://guides.rubyonrails.org/api_app.html"&gt;API-only application with Ruby on Rails&lt;/a&gt;. While Rails is often used to create monolith web applications that serve server-rendered HTML pages, its versatility and rich feature set make it a great choice for building API applications.&lt;/p&gt;

&lt;p&gt;The demand for highly interactive browser applications has prompted a surge in JavaScript front-end frameworks. These frameworks allow developers to shift a significant portion of application logic to the client side, using a RESTful API for interactions with the back-end application server. A web application where JavaScript renders the UI in the browser, which is highly interactive and communicates with the server through an API, is often called a &lt;a href="https://en.wikipedia.org/wiki/Single-page_application"&gt;single-page application (SPA)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll build a &lt;a href="https://en.wikipedia.org/wiki/REST"&gt;REST&lt;/a&gt; API, sometimes called a web API or JSON API. An &lt;a href="https://en.wikipedia.org/wiki/API"&gt;API&lt;/a&gt;, or application programming interface, is a way for two applications or pieces of software to communicate with each other.&lt;/p&gt;

&lt;p&gt;REST APIs allow a client application, typically a SPA running in a web browser, to communicate with the backend application server, which handles the application’s data storage and persistent state.&lt;/p&gt;

&lt;p&gt;The client makes &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP"&gt;HTTP&lt;/a&gt; requests to fetch data from and save data back to the server. The payload of those requests will contain representations of the application’s data. Those representations will often be formatted as JSON, as will be the case here.&lt;/p&gt;

&lt;p&gt;Architecting a web application with an interactive client side alongside a REST API presents both advantages and challenges. A primary benefit is that it allows you to use the same API to build any number of clients: web, mobile, desktop, etc. Additionally, this approach facilitates a clear separation of concerns, decoupling the UI from the data storage and authentication concerns. &lt;/p&gt;

&lt;p&gt;Combining the API and client-side components within a single codebase is feasible, albeit with its own set of considerations. However, in this tutorial, we'll develop the API within its own distinct codebase. Subsequent articles will delve into constructing front-end applications that consume the API using various frameworks, providing a comprehensive understanding of both ends of the development stack.&lt;/p&gt;

&lt;p&gt;In 2024, there are a myriad of options for building a REST API, and Rails remains high on my list. Rails’ extensive feature set and great developer experience allow you to get up and running with an API very quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  About the API
&lt;/h3&gt;

&lt;p&gt;The API we’re about to build will allow us to manage events on a Calendar. Through this API, we’ll be able to create, read, update, and delete calendar events. It’s a &lt;a href="https://en.wikipedia.org/wiki/Create,_read,_update_and_delete"&gt;CRUD&lt;/a&gt; API, but the complexities of calendar events will introduce some additional logic to make things a little more interesting. We’ll use tests to guide us as we develop our API.&lt;/p&gt;

&lt;p&gt;Clone &lt;a href="https://github.com/jmflannery/cal_api"&gt;the repository&lt;/a&gt; yourself and follow along.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install prerequisites
&lt;/h3&gt;

&lt;p&gt;Before we can create the app, we need to install Ruby, the Rails gem, and the PostgreSQL database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Ruby and Rails
&lt;/h3&gt;

&lt;p&gt;Let’s get started. I prefer to manage my Ruby installations on my development machine with &lt;a href="https://github.com/postmodern/chruby"&gt;chruby&lt;/a&gt; paired with &lt;a href="https://github.com/postmodern/ruby-install"&gt;ruby-install&lt;/a&gt;.  Another outstanding set of tools is &lt;a href="https://github.com/rbenv/rbenv"&gt;rbenv&lt;/a&gt; with &lt;a href="https://github.com/rbenv/ruby-build"&gt;ruby-build&lt;/a&gt;. I highly recommend installing Ruby with one of those two sets of tools. Follow the instructions on their project’s READMEs. For this article, I’ll be running Ruby (MRI) v3.3.0.&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;ruby &lt;span class="nt"&gt;-v&lt;/span&gt;
ruby 3.3.0 &lt;span class="o"&gt;(&lt;/span&gt;2023-12-25 revision 5124f9ac75&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;x86_64-darwin23]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Ruby installed, you can now manage your &lt;a href="https://rubygems.org/"&gt;RubyGems&lt;/a&gt; with the &lt;code&gt;gem&lt;/code&gt; command. Install the Rails gem or update if you already have it.&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;gem &lt;span class="nb"&gt;install &lt;/span&gt;rails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or&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;gem update rails
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this article, I’ll be running Rails v7.1.3:&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;rails &lt;span class="nt"&gt;-v&lt;/span&gt;
Rails 7.1.3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install PostgreSQL
&lt;/h3&gt;

&lt;p&gt;A running Rails application needs a database to connect to. You may already have your database of choice installed, but if not, I recommend &lt;a href="https://www.postgresql.org/"&gt;PostgreSQL&lt;/a&gt;, or Postgres for short. On a Mac, probably the easiest way to install it is with &lt;a href="https://postgresapp.com/"&gt;Posrgres.app&lt;/a&gt;. Another option, the one I prefer, is to use &lt;a href="https://brew.sh/"&gt;Homebrew&lt;/a&gt;. With Homebrew installed, this command will install PostgreSQL version 16 along with &lt;a href="https://www.postgresql.org/docs/current/libpq.html"&gt;libpq&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;&lt;span class="nv"&gt;$ &lt;/span&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;libpq postgresql@16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the install finishes, it will display several Caveats to the &lt;code&gt;postgresql@16&lt;/code&gt; install, a few of which are of interest to us; first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;If you need to have postgresql@16 first &lt;span class="k"&gt;in &lt;/span&gt;your PATH, run:
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH="/usr/local/opt/postgresql@16/bin:$PATH"'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While not required for a Rails app to connect Postgres, it is nice to have Postgres’ bin directory in your path because it contains several useful executables for interacting with Postgres, including &lt;code&gt;postgres&lt;/code&gt;, &lt;code&gt;psql&lt;/code&gt;, &lt;code&gt;createdb&lt;/code&gt;, &lt;code&gt;createuser&lt;/code&gt;, and &lt;code&gt;pg_dump&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Add the Postgres bin directory to your path by adding this line to the shell configuration file that loads on shell startup:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/opt/postgresql@16/bin:&lt;span class="nv"&gt;$PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or you can do that with the following command in your terminal:&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;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'export PATH=/usr/local/opt/postgresql@16/bin:$PATH'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, reload the file so that it takes effect.&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;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.bashrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The other, more important caveat is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;To start postgresql@16 now and restart at login:
  brew services start postgresql@16
Or, &lt;span class="k"&gt;if &lt;/span&gt;you don&lt;span class="s1"&gt;'t want/need a background service you can just run:
  LC_ALL="C" /usr/local/opt/postgresql@16/bin/postgres -D /usr/local/var/postgresql@16
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I prefer to manage running Postgres with &lt;code&gt;brew services&lt;/code&gt;. The following command will start up Postgres in the background now and automatically every time your Mac starts up.&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;brew services start postgresql@16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, you mostly won’t have to worry about starting and stopping Postgres, it will always be running in the background. However, you can always restart, start, and stop Postgres with the following &lt;code&gt;brew services&lt;/code&gt; commands:&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;brew services restart postgresql@16

&lt;span class="nv"&gt;$ &lt;/span&gt;brew services stop postgresql@16

&lt;span class="nv"&gt;$ &lt;/span&gt;brew services start postgresql@16

&lt;span class="nv"&gt;$ &lt;/span&gt;brew services list
Name          Status  User File
postgresql@15 started jack ~/Library/LaunchAgents/homebrew.mxcl.postgresql@16.plist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  PostgreSQL databases and roles
&lt;/h3&gt;

&lt;p&gt;With Postgres installed, we can now create any number of databases. A database named &lt;code&gt;postgres&lt;/code&gt; was created for us. Later, we’ll use Rails’ built-in task to create our application’s development and test databases.&lt;/p&gt;

&lt;p&gt;Before Rails can begin interacting with Postgres, we need to specify the database user through which it can access the database. When we installed Postgres, a user (called a role in Postgres) with superuser privileges was created for us, with the same name as our current operating system user. My OS username is &lt;code&gt;jack&lt;/code&gt;. Therefore, I now have a Postgres role named &lt;code&gt;jack&lt;/code&gt;. This can be our app’s database user. Optionally, you can set a password for your user. To do so, launch &lt;a href="https://www.postgresql.org/docs/current/app-psql.html"&gt;psql&lt;/a&gt;, the command-line interface (CLI) tool for interacting with Postgres:&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;psql &lt;span class="nt"&gt;-d&lt;/span&gt; postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because we did not specify one, &lt;code&gt;psql&lt;/code&gt; will attempt to connect as a role under the same name as your OS user. You should have such a role, so there is no need to specify it. &lt;code&gt;psql&lt;/code&gt; will also attempt to connect to a database with the same name as your OS user. In this case, we do not have such a database, so we must pass the &lt;code&gt;-d&lt;/code&gt; flag to connect to the database named &lt;code&gt;postgres&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;At the prompt, use the &lt;code&gt;\password&lt;/code&gt; command to set a new password:&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;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# \password&lt;/span&gt;
Enter new password &lt;span class="k"&gt;for &lt;/span&gt;user &lt;span class="s2"&gt;"jack"&lt;/span&gt;: 
Enter it again: 
&lt;span class="nv"&gt;postgres&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="c"&gt;# \q&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;\q&lt;/code&gt; to quit &lt;code&gt;psql&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Alternatively, you could create a dedicated Postgres role for each application you’re developing. But on your development machine, sharing the same Postgres role for all apps under development is fine.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure bundler for the pg gem
&lt;/h3&gt;

&lt;p&gt;If you installed Postgres via Homebrew, you need to configure bundler so that when it installs the pg gem, it knows where to find the &lt;a href="https://www.postgresql.org/docs/current/app-pgconfig.html"&gt;pg_config&lt;/a&gt; executable, which is installed as part of Postgres. The &lt;a href="https://github.com/ged/ruby-pg"&gt;pg gem&lt;/a&gt; is the Ruby interface to Postgres and requires &lt;code&gt;pg_config&lt;/code&gt; during installation. We can use this command to configure &lt;code&gt;bundler&lt;/code&gt; so that it can find it and successfully install &lt;code&gt;pg&lt;/code&gt;.&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;bundle config &lt;span class="nb"&gt;set &lt;/span&gt;build.pg &lt;span class="nt"&gt;--with-pg-config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/local/opt/postgresql@16/bin/pg_config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Generate the application
&lt;/h3&gt;

&lt;p&gt;This command will generate a new Rails API-only application named &lt;code&gt;cal_api&lt;/code&gt; using PostgreSQL as the database.&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;rails new cal_api &lt;span class="nt"&gt;--api&lt;/span&gt; &lt;span class="nt"&gt;--database&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgresql
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, our app is generated, all dependent gems are installed, and a new git repository is initialized. You can now &lt;code&gt;cd&lt;/code&gt; into the app’s directory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the database connection
&lt;/h3&gt;

&lt;p&gt;Rails needs to know how to connect to your database and expects to find connection information in the &lt;code&gt;config/database.yml&lt;/code&gt; file. When the app was generated with the &lt;code&gt;--database=postgresql&lt;/code&gt; flag, a &lt;code&gt;database.yml&lt;/code&gt; file tailored for Postgres was created for us. It already contains everything we need to get started; we don’t actually need to specify a database user because Postgres will automatically connect as the Postgres role with the same name as our OS user. However, in the name of making the app more configurable, I prefer to set all of the database config values explicitly. Open &lt;code&gt;config/database.yml&lt;/code&gt;, and I recommend reading through and understanding the comments; some valuable information is within. Then replace all of it’s contents with the following:&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;default&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nl"&gt;&amp;amp;default&lt;/span&gt;
  &lt;span class="na"&gt;encoding&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;unicode&lt;/span&gt;
  &lt;span class="na"&gt;adapter&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['DB_ADAPTER'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['DB_USERNAME'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['DB_PASSWORD'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch('DB_HOST') { 'localhost' } %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV['DB_PORT'] %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;pool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch('RAILS_MAX_THREADS') { 5 } %&amp;gt;&lt;/span&gt;

&lt;span class="na"&gt;development&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cal_api_development&lt;/span&gt;

&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cal_api_test&lt;/span&gt;

&lt;span class="na"&gt;production&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*default&lt;/span&gt;
  &lt;span class="na"&gt;database&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cal_api_production&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;As you can see, there are almost no actual values; the values will come from the environment. Rails will automatically process the ERB when it reads the file. We can keep this in the git repo because it doesn’t expose private information.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install dotenv
&lt;/h3&gt;

&lt;p&gt;Storing environment variables for a Rails app can be problematic. The &lt;a href="https://github.com/bkeepers/dotenv"&gt;dotenv&lt;/a&gt; gem will automatically, when Rails boots, load environment variables from &lt;code&gt;.env&lt;/code&gt; files into the Rails &lt;code&gt;ENV&lt;/code&gt;. This is a great way to store private information that varies per developer or deployment environment, such as your development database configuration. Rails Encrypted Credentials is a great way to store private information, like API keys, etc, but I wouldn’t use it for storing my local development environment’s database information. The Encrypted Credentials file is checked into the git repository and would, therefore, be shared by all developers on the project. &lt;code&gt;dotenv&lt;/code&gt; allows each developer or deployment environment to store their own information in &lt;code&gt;.env&lt;/code&gt; files that are ignored by git.&lt;/p&gt;

&lt;p&gt;To install &lt;code&gt;dotenv&lt;/code&gt; add it to your &lt;code&gt;Gemfile&lt;/code&gt;'s &lt;code&gt;development&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; groups.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;group&lt;/span&gt; &lt;span class="ss"&gt;:development&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:test&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;gem&lt;/span&gt; &lt;span class="s1"&gt;'dotenv'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the gem:&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;bundle &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A common strategy is to simply store all environment variables in a &lt;code&gt;.env&lt;/code&gt; file that is kept out of the git repository. That works fine, however, I like to follow the &lt;a href="https://github.com/bkeepers/dotenv?tab=readme-ov-file#should-i-commit-my-env-file"&gt;advice on the dotenv readme&lt;/a&gt; and commit the &lt;code&gt;.env&lt;/code&gt; file to the repository. We’ll take advantage of the &lt;a href="https://github.com/bkeepers/dotenv?tab=readme-ov-file#customizing-rails"&gt;precedence dotenv follows when loading .env.* files&lt;/a&gt; and have each developer maintain a &lt;code&gt;.env.local&lt;/code&gt; file, which we will keep out of the repository. The &lt;code&gt;.env&lt;/code&gt; file will contain all of the variable names without any values and will serve as an example. Any new developer coming onto the project will make a copy of &lt;code&gt;.env&lt;/code&gt; named &lt;code&gt;.env.local&lt;/code&gt; and fill in their own specific database configuration. Rails boots, the values in &lt;code&gt;.env.local&lt;/code&gt; will take precedence over those in &lt;code&gt;.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;First, because by default &lt;code&gt;dotenv&lt;/code&gt; will not load the &lt;code&gt;.env.local&lt;/code&gt; file in the test environment, add the following line of code to your &lt;code&gt;config/application.rb&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Dotenv&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;".env.local"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"RAILS_ENV"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Be sure to add the new line right under the line &lt;code&gt;Bundler.require(*Rails.groups)&lt;/code&gt; near the top of the file like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/application.rb&lt;/span&gt;

&lt;span class="no"&gt;Bundler&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;groups&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Add this line to load .env.local in test&lt;/span&gt;
&lt;span class="no"&gt;Dotenv&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unshift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;".env.local"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"RAILS_ENV"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s2"&gt;"test"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, create a file named &lt;code&gt;.env&lt;/code&gt; at the top level of the project and add the following:&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;DB_ADAPTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="nv"&gt;DB_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="nv"&gt;DB_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then make a copy of &lt;code&gt;.env&lt;/code&gt; called &lt;code&gt;.env.local&lt;/code&gt; with your values filled in:&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;DB_ADAPTER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;postgresql
&lt;span class="nv"&gt;DB_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jack
&lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret
&lt;span class="nv"&gt;DB_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5432
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These four variables must be defined, otherwise Rails will not have a complete database configuration. It's worth it to make these variables required and have Rails fail early on startup if they are not defined. Create the file &lt;code&gt;config/initializers/dotenv.rb&lt;/code&gt; and add the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;development?&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test?&lt;/span&gt;
  &lt;span class="no"&gt;Dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;require_keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"DB_ADAPTER"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"DB_USERNAME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"DB_PASSWORD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"DB_PORT"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way, if the variables are not defined, Rails will fail with a clear error message.&lt;/p&gt;

&lt;p&gt;Ensure that &lt;code&gt;/.env.*&lt;/code&gt; is listed in your &lt;code&gt;.gitignore&lt;/code&gt; file. The base generated &lt;code&gt;.gitignore&lt;/code&gt; file should have the entry &lt;code&gt;/.env*&lt;/code&gt;, which will cause git to ignore &lt;code&gt;.env&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Open your &lt;code&gt;.gitignore&lt;/code&gt; file and change the line&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/.env&lt;span class="k"&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 shell"&gt;&lt;code&gt;/.env.&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With that, git will now see the &lt;code&gt;.env&lt;/code&gt; file, and you can commit it to the repository.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the Databases
&lt;/h3&gt;

&lt;p&gt;If your database is configured properly, you can use the Rails built-in task to create the application's databases. Run the following command in your terminal:&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;rails db:create
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That command should have created the development and test databases, as named in the &lt;code&gt;database.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up Part One
&lt;/h3&gt;

&lt;p&gt;There’s a lot to think about and understand when building an API with Ruby on Rails. Our app is all setup, and now we’re ready to get to the real meat and potatoes of the project. I’m going to break this post up into two parts, otherwise it’d be far too long. In Part Two, we’ll get to building our models, designing the database schema, and building out the API endpoints. We’ll be writing tests along the way and using them as a guide to drive our development. See you in Part Two.&lt;/p&gt;

</description>
      <category>rails</category>
      <category>restapi</category>
      <category>ruby</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Hello World</title>
      <dc:creator>Jack Flannery</dc:creator>
      <pubDate>Tue, 27 Feb 2024 04:30:34 +0000</pubDate>
      <link>https://dev.to/jackf/hello-world-30mj</link>
      <guid>https://dev.to/jackf/hello-world-30mj</guid>
      <description>&lt;p&gt;Hello developers of the world, I’m &lt;a href="https://www.jackflannery.com"&gt;Jack Flannery&lt;/a&gt;, a software dev from New Jersey who decided to start a blog. I’ve been making a living doing web development for over a decade, but I still learn new things most days. So, I’d like to use this space to document some of the knowledge I’ve picked up. From what I understand, it leaks out of the brain through the ears if nothing is done with it.&lt;/p&gt;

&lt;p&gt;What do I plan to write about? One thing I think a lot about these days is how there are approximately infinity frameworks, languages, and platforms for building applications. &lt;a href="https://www.freecodecamp.org/news/the-cure-to-js-fatigue/"&gt;JavaScript fatigue&lt;/a&gt; is real. I have a lot of experience with &lt;a href="https://react.dev/reference/react"&gt;React&lt;/a&gt;, but I want to use this blog to explore other frameworks like &lt;a href="https://svelte.dev/docs/introduction"&gt;Svelt&lt;/a&gt;, &lt;a href="https://vuejs.org/guide/introduction.html"&gt;Vue&lt;/a&gt;, and &lt;a href="https://angular.io/docs"&gt;Angular&lt;/a&gt;. I’m particularly interested in meta frameworks like &lt;a href="https://nextjs.org/docs"&gt;NextJS&lt;/a&gt;, &lt;a href="https://remix.run/docs/en/main"&gt;Remix&lt;/a&gt;, &lt;a href="https://kit.svelte.dev/docs/introduction"&gt;SveltKit&lt;/a&gt;, and &lt;a href="https://nuxt.com/docs/getting-started/introduction"&gt;Nuxt&lt;/a&gt;. And when it comes to the back end, there are even more decisions. What language, which framework? Do you run your own server? Should you use a cloud platform? Should you go server-less? Do you host your own database or use a managed service? These questions need answers. &lt;a href="https://www.jackflannery.com/blog"&gt;My own blog&lt;/a&gt;, where I'll also be posting these articles, is made with NextJS and a &lt;a href="https://guides.rubyonrails.org/api_app.html"&gt;Rails API&lt;/a&gt;. Those are just technologies I am already very familiar with at this time.&lt;/p&gt;

&lt;p&gt;What I want to do is compare things. I want to compare frameworks, learn their pros and cons, and pick favorites. I want to develop the same app in many frameworks and compare the developer experiences. I want to mix and match front and back ends and see what happens. I want to compare bundlers like &lt;a href="https://webpack.js.org/"&gt;Webpack&lt;/a&gt; vs. &lt;a href="https://vitejs.dev/"&gt;Vite&lt;/a&gt; and concepts like &lt;a href="https://en.wikipedia.org/wiki/REST"&gt;REST&lt;/a&gt; vs. &lt;a href="https://graphql.org/"&gt;GraphQL&lt;/a&gt;. I plan to dive deep into how some technologies work under the hood.&lt;/p&gt;

&lt;p&gt;If that sounds boring, I also plan on getting out of my familiar territory and exploring concepts in AI, like models and machine learning. It seems like we in tech have no choice but to try to keep up in this new world of AI; it’s not going away. I also want to have fun with physical computing and write about my &lt;a href="https://en.wikipedia.org/wiki/Internet_of_things"&gt;IoT&lt;/a&gt; projects. I want to turn LEDs on and off and have the smartest home on the block. I want to dive deep into the world of web3 and blockchains. Nah, I'm just kidding about that last one, lol.&lt;/p&gt;

&lt;p&gt;I want to explore these questions and write down what I learn so it’s not lost forever. I want this to be the first of many posts. I want to make writing a habit. I want this blog to have a long life. If this sounds interesting, stay tuned.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>coding</category>
    </item>
  </channel>
</rss>
