How I Wasted Two Days By Not Following Two of the Cardinal Rules of Programming

Cardinal Rule #1: Test Early and Often

My Succotash farm management application was first built as a final capstone project for my time at the Flatiron School with v1.0.1 being released and deployed the day of the final presentation. I had only about 2.5 weeks to get a version that was presentation ready.

With the crazy sprint to go from a framework to multi-model application with more-or-less a one-person team, some standard agile techniques had to fall by the wayside. In the case of Succotash, an obvious candidate was the testing process.

Here we are ten weeks later, and Succotash v1.0 is already deployed on its 8th patch. Many of the recent patches, particularly to the Rails backend, are for the benefit of developers more than users. It is my poor attempt to invite others to get involved. The next step, then, is to add tests to ensure that other developers do not make changes that adversely affect the existing models and controllers.

I decide to use RSpec, read a few blogs, add some new gems that I believe are necessary, (foolishly update the app's Ruby version to 2.7), set up a few very basic tests for the simplest controller class, and run the test... and it all falls apart.

First of all, because of the Ruby version update, I was getting some really ugly stack trace write outs every time I would boot a Rails server or run a Rails console. Some stack overflow searches suggested that it is not a real problem: some syntax patterns had been deprecated and Rails has not updated its own files' syntax to adjust. But I get fed up enough with the stack trace that I revert to the Ruby 2.6.1 version with which Succotash's API originally built.

But my simple RSpec file still was not running. Blogs are telling me to bundle all kinds of new, unfamiliar gems into my API, some of which had outright been renamed. *cough*FactoryGirl.*cough* Considering the recent reports of malicious gems preying on poor spellers, getting the right gems for your project is incredibly important.

Blogs are an awesome resource, especially for Ruby-related topics, given the community's reputation for kindness and helpfulness. But sometimes, it is just better to go to the source, especially when you have 3 blogs are telling you to make 3 different changes to the configuration file that RSpec kindly provides for you when you run rails g rspec:install.

Back to basics, then. The RSpec-Rails repository's readme file on Github has all the necessary information to get RSpec installed with only the features you need, and the RSpec official documentation is easy enough to read to one started on writing tests.

While I did complicate things a bit by using FactoryBot, it did not appear to be the source of any problems. I appeared to be ready to try RSpec again. That's when things really went downhill.

The lesson though is, while it is almost always possible to retrofit basic functionalities and features back into your code after spending months working on improving your project--and this blog has covered one such instance in depth--there is usually a reason that some of these features are built into basic application generators. It can be really hard to debug problems when you add them later, especially when those features' primary purpose is to assist with debugging. My problems with retrofitting RSpec are in hindsight obvious, but I was checking all sorts of configuration files and model classes to get there. There would have been far less ambiguity on where to look had I just started designing tests near the outset.

Cardinal Rule #2: Read the Damn Stack Trace

rspec spec/models/crops_spec.rb

    
An error occurred while loading ./spec/models/crops_spec.rb.
Failure/Error: require File.expand_path('../config/environment', __dir__)

TypeError:
    no implicit conversion of nil into String
# /Users/mbechtel/.rvm/gems/ruby-2.6.1/gems/rack-cors-1.1.1/lib/rack/cors.rb:300:in `quote'
# /Users/mbechtel/.rvm/gems/ruby-2.6.1/gems/rack-cors-1.1.1/lib/rack/cors.rb:300:in `block in origins'
# /Users/mbechtel/.rvm/gems/ruby-2.6.1/gems/rack-cors-1.1.1/lib/rack/cors.rb:293:in `map'
# /Users/mbechtel/.rvm/gems/ruby-2.6.1/gems/rack-cors-1.1.1/lib/rack/cors.rb:293:in `origins'
# ./config/initializers/cors.rb:10:in `block (2 levels) in main'
# /Users/mbechtel/.rvm/gems/ruby-2.6.1/gems/rack-cors-1.1.1/lib/rack/cors.rb:60:in `instance_eval'
# /Users/mbechtel/.rvm/gems/ruby-2.6.1/gems/rack-cors-1.1.1/lib/rack/cors.rb:60:in `allow'
# ./config/initializers/cors.rb:9:in `block in main'
# /Users/mbechtel/.rvm/gems/ruby-2.6.1/gems/rack-cors-1.1.1/lib/rack/cors.rb:45:in `instance_eval'
# /Users/mbechtel/.rvm/gems/ruby-2.6.1/gems/rack-cors-1.1.1/lib/rack/cors.rb:45:in `initialize'
...
    

The first line here (File.expand_path(...)), comes from the rails_helper.rb file that is created when running the command rails g rspec:install. It had come up often in my earlier debugging as well, because one of those final arguments reaches down into the depths of Rails' system and pokes one of the Rails methods using a syntax that was deprecated in Rails 2.7. It is a single line that was for a couple days extremely confounding. Since this is a line of code that, as a provided configuration, I was trying ever so hard to avoid editing.

This, of course, led me on another search through Stack Overflow. I stumbled upon this Stack Overflow answer, which works, but was not an answer that I found to be sufficient, nor was the questioner's stack trace the same as mine. At the very least, it allowed me to finally work on my actual tests. But, again, if my goal is to set up tests for the benefit of other developers, asking them to run a non-standard command to run tests was not an acceptable solution.

I could finally test my tests, and that was the main priority for a while. It took some time to return to the issue and a lot of unnecessary bundle exec rake spec in the meantime.

A poor configuration somewhere? More problems with deprecated methods? Not understanding how some other gems like FactoryBot or Database-Cleaner mess with the system? The scenarios were pretty hard to debug for someone really just trying to get RSpec working for the first time. Perhaps it is best to ask for help...

or... Let's look at that stack trace again. If you have done any development in Rails, you have probably seen a similar output a couple dozen times. It is the message that appears whenever you have a problem with your CORS configuration. But we're not working in an environment that needs to limit access. Tests are run in the test environment to preserve our development database. Why would CORS be an issue?

Because the Rails application has an initializer that requires a string as an allowed origin and will not boot in any circumstance without one. The development and production environments are still working, but the test environment is not. The stack trace points us to cors.rb line 10.

And the application.yml file that Figaro is looking at:

And there it is, just like the stack trace was telling us all along. Rails cannot convert nil to a string, and because the allowed_origin variable for the test environment is nil, CORS does not know how to handle HTTP requests. Adding an allowed_origin variable for the test environment solves the problem.

I spent a total of two days squashing these bugs despite it really only taking following one of two cardinal rules of programming to solve or shorten the amount of wasted time. I could have either started testing when the application was smaller or simply read the damn stack trace, and my problems would have been limited to a few hours at most.