Cademy logoCademy Marketplace

Course Images

Testing Ruby with RSpec: The Complete Guide

Testing Ruby with RSpec: The Complete Guide

🔥 Limited Time Offer 🔥

Get a 10% discount on your first order when you use this promo code at checkout: MAY24BAN3X

  • 30 Day Money Back Guarantee
  • Completion Certificate
  • 24/7 Technical Support

Highlights

  • On-Demand course

  • 7 hours 30 minutes

  • All levels

Description

In this course, we will master the syntax and structure of RSpec then learn to utilize test-driven development principles to design and implement clean test specs and reduce dependencies in the test suite by mocking objects with class and instance doubles. We will also explore the wide collection of RSpec matches available to test the code.

This course offers a comprehensive overview of the RSpec testing library for the Ruby programming library. RSpec is the most popular Ruby Gem of all time, with over 300 million downloads to date. If you are new to the topic, testing is the practice of writing code that confirms that other code works as expected. Tests control regressions, which are changes to the code that breaks the program. The benefits of testing extend beyond the codebase. Adopting a test-driven approach will also make you a better developer. Tests force you to think critically about a program and its features: classes, objects, methods, and more. This course begins with the essentials and proceeds to more complex topics, including installation, project initialization, Test-Driven Development (TDD), let variables, before and after hooks, subjects, shared examples, shared context, built-in matches, mocks and doubles, instance doubles, and class doubles. At the completion of this course, you will have acquired hands-on Ruby testing experience with this awesome RSpec library. All Resources for this course are available at https://github.com/packtpublishing/testing-ruby-with-rspec-the-complete-guide

What You Will Learn

Learn how to install RSpec and initialize the project
Cover Test-Driven Development (TDD), methods, and hooks
Learn about subjects, shared examples, and shared context
Explore built-in matchers such as not_to, all, eq, and more
Learn to create a test double
Understand instance doubles and class doubles

Audience

Programmers who want to explore the fundamentals of testing and TDD and intermediate Ruby developers interested in upgrading their skillset will benefit from this course. Knowing any text editor (VS Code is recommended) would be beneficial. No previous testing experience is needed!

Approach

This course is designed in such a way that each section covers a new scenario and adopts a step-by-step approach to help you learn and understand the concepts.

Key Features

Master the syntax and structure of RSpec, the most popular Ruby Gem for testing * Explore the wide collection of RSpec matches available to test your code * Utilize test-driven development principles to design and implement clean test specs in Ruby

Github Repo

https://github.com/packtpublishing/testing-ruby-with-rspec-the-complete-guide

About the Author

Boris Paskhaver

Boris Paskhaver is a NYC-based web developer and software engineer with experience in building apps in React/Redux and Ruby on Rails. Raised in New Jersey, he graduated from the Stern School of Business at New York University in 2013 with a double major in business economics and marketing. Since graduation, his work has taken him in a wide variety of directions-he spent years in marketing, then financial services, and now the tech industry. He has worked everywhere, from a 50-person digital agency to an international tech powerhouse with thousands of employees. He always had a love of learning but struggled with the traditional resources available for education. His goal is to create comprehensive courses that break down complex details into small, digestible pieces.

Course Outline

1. Introduction

This section will give you a brief introduction to the course.

1. Welcome to RSpec

Welcome to the course! This lecture offers a quick introduction to the RSpec Gem as well as the benefits of testing. The various pieces of the library -- the core runner, expectations, and mocks -- are also introduced.

2. Unit Tests versus End-to-End (E2E) Tests

Unit tests target a specific "unit" or piece of an application, such as a single class or method. They create isolation between coupled components to ensure each piece stands by itself. In comparison, end-to-end tests test an application or a large feature as a whole. Integration tests fall somewhere in the middle. In this lesson, we talk extensively about these types of tests and introduce the testing pyramid.

3. Installing RSpec

Ruby ships with a package manager called Gem. In this lesson, we utilize it to download the RSpec testing library.

4. Starting a Project with rspec --init

The rspec --init command creates a base skeleton for an RSpec project. It creates a spec_helper.rb file where high-level, global RSpec settings for the project can be declared. There are some recommended settings that can be enabled by removing the =begin and =end lines in the file.

5. Test-Driven Development

Test-Driven Development (TDD) is a testing paradigm that argues that tests should be written first, before the code. This approach forces the developer to think critically about the implementation of the feature, one method at a time. This lesson describes the red-green-refactor pattern for practicing TDD. Red means a failing spec, green means a passing spec, and refactor means optimizing the code for clarity and efficiency.

6. The Describe Method

The describe method on the top-level RSpec module creates an example group. An example group contains one or more examples. An example is the technical RSpec term for a test. In this lesson, we walk through the basic syntax and structure of setting up a sample example group (hey, that rhymes!)

7. The it Method

The 'it' method creates an example (that is, a focused test). The 'it' method accepts a string -- ideally, it should be one that makes the example read like a sentence (that is, it "should shuffle the deck" or it "can communicate with database"). After the string argument, pass the method a block. The block will contain the assertions for that example.

8. The Expect and Eq Methods

The expect method accepts what will be evaluated --it can be an object, a class, a method, or a plain Ruby expression like 1 + 1. The method returns an object that includes a to method, which is invoked with a matcher. A matcher is a type of assertion; there are various matchers in RSpec for equality, inequality, identity, inclusion and more. In this lesson, we create a sample Card object and write our first expectation for it.

9. Reading Failures

The Rspec command in the terminal can be followed with a path to a spec file's location. The command starts the RSpec test runner. The terminal output includes a list of all examples that failed, including the line number that encountered an error or inconsistency. In this lesson, we read over the output and prepare to fix our failing specs.

10. Making the Specs Pass

It's time to make the Card class a reality! In this lesson, using our failing specs to guide us, we define a new Card class with a type attribute and a public reader method. It's important to not speed ahead and try to fix everything at once. Use each failure from the RSpec output to determine the next line of code to write. Fix one error, then run the Rspec command again, and repeat.

11. Multiple Examples in Example Group

An example group can contain multiple examples. Some testing advocates argue that each example should only have one assertion. In this lesson, we expand the Card class to have a rank and a suit and write the corresponding specs.

12. Fixing Failing Specs Again

In this lesson, we continue the practice of using our failing specs to drive the development of the code. We updated the Card class to have two initialize arguments, two reader methods, and two instance variables. We also discuss some of the drawbacks of our current examples, particularly the duplication.

13. Reducing Duplication - Before Hooks and Instance Variables

Multiple examples in a test suite often rely on the same piece of data or a common object. Duplicating the code to create or access that object in each example creates a lot of duplication. To DRY up the code, this lesson introduces before hooks and instance variables. A hook is a piece of code that runs automatically at a specific time during testing. By default, the before hook executes a block of code before each example. Within the block, we assign values to instance variables to preserve values once the block execution ends. These instance variables are then available to be used within any examples defined below.

14. Reducing Duplication: Helper Methods

The block passed to the describe method provides a regular environment for Ruby expressions. We can define helper methods there to be invoked by the examples. Like a before block, this approach also creates separation between the examples.

15. Problems with Mutation

In this lesson, we discuss the limitations of a helper method approach. Subsequent calls to the same helper method in an example will return a new object each time. This makes it impossible to mutate a single object's state without using extra variables.

16. Reducing Duplication: The let Method

The let method defines a memoized, lazy-loaded variable that is available to all examples in the current context. The method accepts a symbol for the name of the variable and a block for the value that variable should be assigned. Lazy-loaded means the variable will not be declared until it is used in a specific example. Memoized means multiple references to the variable in the same example will not require a reevaluation of the block passed to the let method.

17. Custom Error Messages

To have RSpec display a custom error message when an expectation fails, pass a string as the second argument to the to method. In this lesson, we compare the different terminal outputs for the default error message and our custom one.

18. The context Method and Nested Describes

Describe method invocations can be nested inside other describe blocks. This is done to provide context about the specific circumstance or situation that an example is being run. context is an available alias for the describe method -- it can be used in the same fashion. In this lesson, we create nested describe blocks to test the even and predicate method in Ruby.

19. Before and After Hooks

A hook allows the developer to run code during certain moments or events in the test suite's execution. Before and after running a block of code before or after a given criteria. When given a symbol of: example, the hook will run before / after each individual test. When given a symbol of: context, the hook will run once before / after all tests in the current block. In a real-life scenario, context is used for high-level setup and teardown operations like connecting and disconnecting to a database (which is an expensive operation that you don't want to perform for each test).

20. Nested Logic: Hooks

Hooks gain an additional layer of complexity when describe / context blocks are nested within other describe / context blocks. The before(: context) hooks runs once before all tests in the current context (i.e. the current block). The before(:example) hook runs once before each test in the current context (which includes all nested blocks as well). If there is a before(:example) hook defined at multiple levels, each one will run in sequence, starting from the top-most block and proceeding downwards.

21. Nested Logic: Overwriting Let Variables

Let variables can be set to different values in nested blocks. Ruby / RSpec will search for the name in the current scope, then proceed upwards if it is unable to find it. Instead of using multiple variable names and adding confusion, it's much more elegant to reuse the let variable and assign it new, relevant values in each testing context.


2. Subjects, Shared Examples, and Shared Context

In this section, we'll learn about subjects, shared examples, and shared context.

1. Implicit Subject

The subject helper method lazily instantiates an instance of the class under test. This offers the developer several advantages including (1) one less let variable and (2) shorthand syntax for expectations that we'll discuss later. By default, subject assumes the class will have no initialization arguments. Within the same example, subject will memoize / cache the object when it's used multiple times. Between different examples, subject will instantiate a fresh object. In this lesson, we utilize subject to test a Ruby hash.

2. Explicit Subject

The subject method can be invoked outside of any example. The last evaluation of the block passed to the method will serve as its return value. This allows the developer to instantiate a custom object from the class under test to serve as the base "subject" for testing. A subject declared in an outer scope (that is an outer block) will be available to all inner, nested blocks.

3. described_class

When a class is passed as an argument to the describe method, the class itself becomes available via the described_class method. It is advantageous to use described_class whenever the class is referenced because it makes the specs more adaptable to changes in business logic, such as the renaming of the class under test.

4. One-Liner-Example-Syntax

The use of either an implicit or explicit subject grants access to a special one-liner syntax for writing an example. First, the it method is passed a block without a string argument. Next, the method is_expected is invoked as a replacement for the expect syntax. Finally, a regular RSpec matcher (such as eq) is attached to the end. In this lesson, we demonstrate both syntactical options side by side.

5. Shared Examples with include_examples

The RSpec.shared_examples method is used to define commonly used examples that can be injected into multiple example groups. The method is passed a string identifier and a block where all the examples are declared. Later, the include_examples is invoked within the body of an example group and passed the string identifier. Multiple shared examples can be used; it is common to store these in a separate helper file that is imported by other spec files.

6. Shared Context with include_context

A complement to shared examples, the shared_context method allows for multiple example groups to rely on common boilerplate code. The developer can reduce duplication by declaring before blocks, instance variables, helper methods, and let variables in a shared context. The include_context method is then used to inject that context into multiple example groups.


3. Built-In Matchers

In this section, you'll learn about Built-In Matchers.

1. The not_to Method

This section is focused on the matchers available in RSpec. A matcher is a type of assertion, expectation or validation. RSpec includes a variety of matchers -- everything from equality to inclusion to identity to error raising. The not_to method is an alternative to the to method that checks for the inverse or negative of a given matcher. For example, not_to eq will check for inequality.

2. Equality Matchers I (eq and eql)

The eq matcher checks only for value equality. The stricter eql matcher checks for both value and data type. For example, eq would consider the integer value 3 and the floating point value 3.0 to be equal. eql would not consider the two equal because they are of different data types.

3. Equality Matchers II (equal and be)

The equal matcher checks that two values are identical. To be identical, the values must both be the same object in the computer's memory. Identity is the strictest form of equality. In this lesson, we apply these matchers to arrays and introduce a real-life analogy of houses in a neighborhood.

4. Comparison Matchers

RSpec includes support for numerical comparisons like greater than and less than or equal to. Use the be matcher followed by the proper Ruby operator. For example, expect(10).to be >= 5 will pass if 10 is found to be greater than or equal to 5.

5. Predicate Matchers

A predicate method is one that returns a Boolean value -- either a true or a false. RSpec includes dynamic matchers for testing predicate methods. For any method, remove the question mark from the name and prefix it with be_. For example, the empty? method on an array can be tested with the predicate matcher be_empty.

6. all Matcher

The all matcher performs an aggregate check --- it validates that all elements in array fit some criteria. For example, expect ([2, 4, 6]).to all(be_even) asserts that each value in an array is even. The matcher can also accept a mathematical comparison like be < 100 or be >= -2.

7. be Matcher (Truthy, Falsy and Nil Values)

Ruby has two falsy values: false and nil. All other objects are considered truthy, which means that they evaluate to true in a conditional context. In this lesson, we introduce the be_truthy and be_falsy matchers for asserting truthiness / falsiness as well as the be_nil matcher for explicit comparisons to nil.

8. change Matcher

One of the most powerful matchers in RSpec, the change matcher compares the value of something before and after an operation. Pass the expect method a block with some code that may mutate an object's state. Afterwards, invoke the change matcher with another block that describes what value is being tracked. Several comparison methods are available: the from / to combo as well as by.

9. contain_exactly Matcher

The contain_exactly matcher asserts that a sequence of elements is found within a collection, irrespective of order. The method is passed a comma-separated list of arguments to search for. If order matters, a more equality-focused matcher like eq would be used.

10. start_with and end_with Matchers

The start_with and end_with matchers check for the presence of a smaller subset of elements at the beginning or end of a data structure. For a string, the matchers will check for a substring of characters. For an array, the matchers will check for the presence of elements in sequential order.

11. have_attributes Matcher

The have_attributes matcher confirms that an object has a set of attributes with corresponding values. The method accepts a hash where the key is the name of the attribute and the value is the corresponding attribute value. When testing a custom object, the attributes have to be publicly accessible via reader methods to be able to be checked.

12. include Matcher

The include matcher checks for the inclusion of an object within a larger object. It can be used to check for a substring within a string, a value within an array, a key within a hash, and even a key-value pair within a hash. This matcher is not concerned with order, only with presence.

13. raise_error Matcher

The raise_error matcher checks that a block of code triggers an error. It is recommended to pass the matcher a specific error to watch out for. Otherwise, it becomes difficult to ascertain whether the expected error was the one that caused the spec to pass.

14. respond_to Matcher

The respond_to matcher verifies that an object can receive one or more given methods. The implementation and return values of the methods is not considered; this matcher checks only that the object has a method defined on its public interface. The additional with method confirms that the method is called with a specific number of arguments.

15. satisfy Matcher

The satisfy matcher is used to define custom assertions. This offers the best opportunity for defining expectations that tie into the business logic of your app. In this lesson, we use satisfy to check if a string is a palindrome.

16. not_to Method

The not_to method tests for the negative or inverse of any RSpec matcher. For example, not_to include affirms that an array does not include a value. In this lesson, we practice the syntax while reviewing many of the matchers introduced in this section.

17. Compound Expectations

Compound expectations can chain together two or more expectations. The and method ensures both of the matchers pass while the or method ensures either of the matchers pass. In this lesson, we explore the syntax in the context of a randomness problem.


4. Mocks

In this section, you'll learn about Mocks.

1. Create a Test Double

A double is a stand-in for a real object in a test. Doubles allow us to isolate specs to test app components (such as classes) independently. In this lesson, we create a sample stuntman double and walk through the syntax to permit and receive methods on it.

2. Set Up Our Test Movie

In this lesson, we do some light prep work to set up the upcoming tests. All examples are tied to the real-life situation of a movie set with real actors and stunt doubles.

3. Replacing an Object with a Double

In this lesson, we replace our real object with a test double, connecting the ideas introduced in the last two lessons. The allow method defines a mock method on a double and the expect method affirms that it is called.

4. Receive Counts

In this lesson, we introduce new methods such as at_most, at_least, once, and exactly to test how many times a method was received on a double. The method counts are not independent between different assertions so these methods cannot be stacked.

5. The allow Method

The allow method is incredibly versatile -- it can be customized to respond to different arguments as well as return different return values. In this lesson, we use the method to create a double that mocks the functionality of a Ruby array.

6. Matching Arguments

A method stub's return value can be customized depending on the number or arguments, the type of arguments, or specific argument values. The with method specifies the number of expected arguments. In this lesson, we continue exploring how we can modify the allow method.

7. Instance Doubles

Verifying doubles are stricter alternatives to normal doubles. RSpec checks that the methods being stubbed are present on the underlying object. The instance_double method takes a class name or object as its argument. It verifies any method being stubbed would be present on an instance of that class.

8. Class Doubles

Class doubles mock a class and its class methods rather than its instance methods. One advantage of the class_double method is that the class itself must be defined -- this allows for test-driven development.

9. Spies I

Spies are an alternate type of test double. They spy / observe the behavior of an object and all of the methods it receives. Spies follow a different pattern than a double -- they assert a message has been received after an action.

10. Spies II

In this lesson, we practice using spies with a Garage / Car class combo.


5. Conclusion

This section will give you a brief introduction of what we have learnt so far.

1. Conclusion

Congratulations on making it to the end of the course! I hope the course has been as fun to watch as it was for me to produce. Best of luck in all your coding endeavors!

Course Content

  1. Testing Ruby with RSpec: The Complete Guide

About The Provider

Packt
Packt
Birmingham
Founded in 2004 in Birmingham, UK, Packt’s mission is to help the world put software to work in new ways, through the delivery of effective learning and i...
Read more about Packt

Tags

Reviews