From 581fcfad4c2c43995d0efd9189f026f236b9f8c1 Mon Sep 17 00:00:00 2001 From: Roman Pushkin Date: Sat, 5 Sep 2020 15:51:21 -0700 Subject: [PATCH] Save --- manuscript/091.txt | 95 +++++++++++++++++++ manuscript/images/091-testing-frameworks.png | Bin 0 -> 27539 bytes 2 files changed, 95 insertions(+) create mode 100644 manuscript/images/091-testing-frameworks.png diff --git a/manuscript/091.txt b/manuscript/091.txt index e69de29..5307ade 100644 --- a/manuscript/091.txt +++ b/manuscript/091.txt @@ -0,0 +1,95 @@ +## Rspec + +According to [The Ruby Toolbox](https://www.ruby-toolbox.com/categories/testing_frameworks) website, Rspec is the most popular testing library for Ruby: + +{width=100%} +![Ruby testing frameworks, Rspec is the most popular](images/091-testing-frameworks.png) + +Some folks from Ruby community don't like it though: + +> RSpec offends me aesthetically with no discernible benefit for its added complexity over test/unit. + +[DHH on Twitter](https://twitter.com/dhh/status/52807321499340800) + +It's actually a matter of preference, the question is quite controversial. To some degree DHH is right, RSpec often emphasizes the smart, not simple way of writing tests. It feels like a sports car changing lanes fast on a highway, but at the end result is about the same. + +The Rspec configuration can take up some time, the good news is that Rspec has completed its maturing phase, and configuration is rather simple, and for almost every question there is an answer you can search online. + +In reality, the readability (and developer happiness) of tests solely depends on a team. A development tool is not the first factor here. There is no such tool that solves all the problems, and any tool can be misused. The question is more about balance and producing simple and readable tests rather than smart tests. + +Rspec is based on its own DSL - domain-specific language. Read it as "language extensions". Rspec introduces few new keywords, like "describe", "let", "it", "before", "after" and so on. We haven't covered any DSL in this book before, so let's just assume it's a new language syntax that you need to be familiar with. + +We are going to install and configure Rspec from scratch. Let's start with a latest Ruby, type "`rvm list known`" to get the list of available rubies for download. We need "MRI" flavor (Matz's Ruby Interpreter - classic Ruby developed by Yukihiro Matsumoto). Once you get the version, type the command to install: + +``` +$ rvm install 2.7.0 +``` + +Or any other version without "-preview" suffix. Then let's create a directory and fix the Ruby version for this directory: + +``` +$ mkdir rspec_demo +$ cd rspec_demo +$ echo "2.7.0" > .ruby-version +``` + +Make sure you have the version you expect: + +``` +$ ruby -v +ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin16] +``` + +If the version is different, do cd-trick. Change directory to one level up and switch back, that's how some Ruby version managers work, they hijack your "`cd`" command: + +``` +$ cd .. +$ cd rspec_demo +``` + +In previous chapters we were using "`gem install...`" command to download and use Ruby libraries from Internet. Once a library gets downloaded, it will be located somewhere on the disk, and programs can use it. However, it's nice if we can keep a list of dependencies somewhere. Like "hey, here is our program, but it depends on this, this and this, you must download these libraries, so the program works file". + +"`Gemfile`" is such mechanism. It's just a list of libraries, with some features. For example, you can specify a source, or you can stick to particular version of a library. Imagine new versions gets released, and API (the way you use the library) changes. You still want your old program to work, right? Let's create new "`Gemfile`": + +``` +$ bundle init +``` + +Here is how default "`Gemfile`" looks like: + +``` +# frozen_string_literal: true + +source "https://rubygems.org" + +git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } + +# gem "rails" +``` + +Looks a bit scary, but at the moment we need only one line that defines a place where to look for gems. We'll keep the "`source ...`" line and add "`rspec`" dependency. So here is how your "`Gemfile`" should look like: + +``` +source "https://rubygems.org" +gem "rspec" +``` + +And we can run "`bundle`". Basically this command will execute "`gem install ...`" for every gem located in "`Gemfile`" (we have only one at the moment): + +``` +$ bundle +Fetching gem metadata from https://rubygems.org/... +Resolving dependencies... +... +Fetching rspec 3.9.0 +Installing rspec 3.9.0 +Bundle complete! 1 Gemfile dependency, 7 gems now installed. +Use `bundle info [gemname]` to see where a bundled gem is installed. +``` + +If you "`ls`" in the directory, you'll see "`Gemfile.lock`" which was created automatically. You're not supposed to modify this file with your code editor, it gets updated automatically by "`bundle`" command if needed. Lock-file locks your application to specific version of library, so the app will work forever. Imagine in 30 years from now you'll need to execute the same program. Your "`Gemfile.lock`" comes into play and says: "I don't care which year it is now, and I don't care about any newly released software, I need to download these versions from Internet". Unless you explicitly update your dependencies, you'll stick to the specific version of Rspec (or any other library). + +You should have three files in your directory: "`.ruby-version`", "`Gemfile`", and "`Gemfile.lock`". Type "`ls -a`" to get the full list. Yes, you need to specify "`-a`" (all) parameter, so the list has hidden (dot) files as well, including "`.ruby-version`". + +If you type "`gem which rspec`" you'll see the path where exactly Rspec has been downloaded. It's outside your project, and it is understandable, because you might want to use it in other project on the same computer. + diff --git a/manuscript/images/091-testing-frameworks.png b/manuscript/images/091-testing-frameworks.png new file mode 100644 index 0000000000000000000000000000000000000000..632ad7db52e507e7c78ace8df6607e223b5acc3f GIT binary patch literal 27539 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