From 2b7b3d6e2fd87cfa3fc620aa0b51db399ed5a14b Mon Sep 17 00:00:00 2001 From: Rachel Moser Date: Sun, 19 Sep 2021 17:28:17 -0500 Subject: [PATCH 1/2] Feature: Add testing exercises for RSpec lessons Because: * The new RSpec testing lessons needs corresponding exercises. This Commit: * Adds 5 RSpec Matcher exercises for Testing Basics lesson. * Adds 2 RSpec exercises for Testing Part 2 lesson. * Updates RSpec version. * Adds rubocop, but ignores the ruby_basics directory. * Adds README for the ruby_testing directory. --- .rubocop.yml | 6 ++ Gemfile | 5 +- Gemfile.lock | 45 ++++++++--- ruby_testing/1_rspec_basics/.rspec | 1 + .../1_rspec_basics/spec/1_eq_matcher_spec.rb | 16 ++++ .../spec/2_include_matcher_spec.rb | 19 +++++ .../spec/3_be_true_false_spec.rb | 35 ++++++++ .../spec/4_be_nil_matcher_spec.rb | 18 +++++ .../spec/5_contain_exactly_spec.rb | 15 ++++ .../1_rspec_basics/spec/spec_helper.rb | 19 +++++ ruby_testing/2_rspec_techniques/.rspec | 1 + .../spec/1_equality_matchers_spec.rb | 62 +++++++++++++++ .../spec/2_lesson_practice_spec.rb | 79 +++++++++++++++++++ .../2_rspec_techniques/spec/spec_helper.rb | 19 +++++ ruby_testing/README.md | 27 +++++++ 15 files changed, 354 insertions(+), 13 deletions(-) create mode 100644 .rubocop.yml create mode 100644 ruby_testing/1_rspec_basics/.rspec create mode 100644 ruby_testing/1_rspec_basics/spec/1_eq_matcher_spec.rb create mode 100644 ruby_testing/1_rspec_basics/spec/2_include_matcher_spec.rb create mode 100644 ruby_testing/1_rspec_basics/spec/3_be_true_false_spec.rb create mode 100644 ruby_testing/1_rspec_basics/spec/4_be_nil_matcher_spec.rb create mode 100644 ruby_testing/1_rspec_basics/spec/5_contain_exactly_spec.rb create mode 100644 ruby_testing/1_rspec_basics/spec/spec_helper.rb create mode 100644 ruby_testing/2_rspec_techniques/.rspec create mode 100644 ruby_testing/2_rspec_techniques/spec/1_equality_matchers_spec.rb create mode 100644 ruby_testing/2_rspec_techniques/spec/2_lesson_practice_spec.rb create mode 100644 ruby_testing/2_rspec_techniques/spec/spec_helper.rb create mode 100644 ruby_testing/README.md diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000000..f3495a234c --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,6 @@ +AllCops: + NewCops: enable + Exclude: + - 'ruby_basics/**/*' +Style/Documentation: + Enabled: false diff --git a/Gemfile b/Gemfile index bc63d8fb1f..b7710cd9f8 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,6 @@ +# frozen_string_literal: true + source 'https://rubygems.org' -gem 'rspec', '~>3.9' \ No newline at end of file +gem 'rspec', '~> 3.10' +gem 'rubocop', '~> 1.21' diff --git a/Gemfile.lock b/Gemfile.lock index bbce5c370e..b0a2d69f64 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,26 +1,47 @@ GEM remote: https://rubygems.org/ specs: + ast (2.4.2) diff-lcs (1.4.4) - rspec (3.9.0) - rspec-core (~> 3.9.0) - rspec-expectations (~> 3.9.0) - rspec-mocks (~> 3.9.0) - rspec-core (3.9.2) - rspec-support (~> 3.9.3) - rspec-expectations (3.9.2) + parallel (1.21.0) + parser (3.0.2.0) + ast (~> 2.4.1) + rainbow (3.0.0) + regexp_parser (2.1.1) + rexml (3.2.5) + rspec (3.10.0) + rspec-core (~> 3.10.0) + rspec-expectations (~> 3.10.0) + rspec-mocks (~> 3.10.0) + rspec-core (3.10.1) + rspec-support (~> 3.10.0) + rspec-expectations (3.10.1) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-mocks (3.9.1) + rspec-support (~> 3.10.0) + rspec-mocks (3.10.2) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.9.0) - rspec-support (3.9.3) + rspec-support (~> 3.10.0) + rspec-support (3.10.2) + rubocop (1.21.0) + parallel (~> 1.10) + parser (>= 3.0.0.0) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 1.8, < 3.0) + rexml + rubocop-ast (>= 1.9.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 1.4.0, < 3.0) + rubocop-ast (1.11.0) + parser (>= 3.0.1.1) + ruby-progressbar (1.11.0) + unicode-display_width (2.1.0) PLATFORMS ruby DEPENDENCIES - rspec (~> 3.9) + rspec (~> 3.10) + rubocop (~> 1.21) BUNDLED WITH 2.1.4 diff --git a/ruby_testing/1_rspec_basics/.rspec b/ruby_testing/1_rspec_basics/.rspec new file mode 100644 index 0000000000..279bfd9e99 --- /dev/null +++ b/ruby_testing/1_rspec_basics/.rspec @@ -0,0 +1 @@ +--require spec_helper --format documentation --color diff --git a/ruby_testing/1_rspec_basics/spec/1_eq_matcher_spec.rb b/ruby_testing/1_rspec_basics/spec/1_eq_matcher_spec.rb new file mode 100644 index 0000000000..deef7bc991 --- /dev/null +++ b/ruby_testing/1_rspec_basics/spec/1_eq_matcher_spec.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +def generate_hashtag(str) + tag = str.split.map(&:capitalize).join + return nil unless tag.length.between?(1, 139) + + "##{tag}" +end + +RSpec.describe 'Eq Matcher Exercise' do + # Write a test that generate_hashtag('The Odin Project') eq '#TheOdinProject' + + # Write a test that generate_hashtag('Ruby on Rails') eq '#RubyOnRails' + + # Write a test that generate_hashtag('Join our Discord Community') eq '#JoinOurDiscordCommunity' +end diff --git a/ruby_testing/1_rspec_basics/spec/2_include_matcher_spec.rb b/ruby_testing/1_rspec_basics/spec/2_include_matcher_spec.rb new file mode 100644 index 0000000000..8b0a6d4d39 --- /dev/null +++ b/ruby_testing/1_rspec_basics/spec/2_include_matcher_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +def create_range(min, max) + (min..max).to_a +end + +RSpec.describe 'Include Matcher Exercises' do + # Write a test that create_range(0, 5) includes 0, 1, 2, 3, 4, 5 + + # Write a test that create_range(0, 5) includes 3 + + # Write a test that create_range(0, 5) does not include 11 + + # Write a test that create_range(5, 10) includes 5, 6, 7, 8, 9, 10 + + # Write a test that create_range(5, 10) includes 5, 7, 9 + + # Write a test that create_range(5, 10) does not include 0 or 1 +end diff --git a/ruby_testing/1_rspec_basics/spec/3_be_true_false_spec.rb b/ruby_testing/1_rspec_basics/spec/3_be_true_false_spec.rb new file mode 100644 index 0000000000..ed0c9d4d6e --- /dev/null +++ b/ruby_testing/1_rspec_basics/spec/3_be_true_false_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +RSpec.describe 'Be True False Matcher Exercises' do + describe 'be truthy and falsy matcher' do + # Using the be matcher, write a test that any string, for example 'hello' is truthy + + # Using the be matcher, write a test that any number, for example -100 is truthy + + # Using the be matcher, write a test that any array, for example [] is truthy + + # Using the be matcher, write a test that true is truthy + + # Using the be matcher, write a test that false is falsy + + # Using the be matcher, write a test that nil is falsy + end + + # As you can see, almost all values are truthy, with the exception of false and nil. + # Therefore do not use 'be_truthy' or 'be_falsy' for methods that should only be evaluated into a Boolean. + # https://eddyluten.com/rspec-be_truthy-exists-or-be-true + + def palindrome?(word) + word.reverse == word + end + + describe 'be true and false matcher' do + # Using the be matcher, write a test that palindrome?('cat') is false + + # Using the be matcher, write a test that palindrome?('level') is true + + # Using the be matcher, write a test that palindrome?('racecar') is true + + # Using the be matcher, write a test that palindrome?('odin') is false + end +end diff --git a/ruby_testing/1_rspec_basics/spec/4_be_nil_matcher_spec.rb b/ruby_testing/1_rspec_basics/spec/4_be_nil_matcher_spec.rb new file mode 100644 index 0000000000..c32df245e5 --- /dev/null +++ b/ruby_testing/1_rspec_basics/spec/4_be_nil_matcher_spec.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +def generate_hashtag(str) + tag = str.split.map(&:capitalize).join + return nil unless tag.length.between?(1, 139) + + "##{tag}" +end + +RSpec.describe 'Be Nil Matcher Exercises' do + # Using the be matcher, write a test that generate_hashtag('') is nil + + # Using the be matcher, write a test that generate_hashtag('a' * 140) is nil + + # Using the be matcher, write a test that generate_hashtag(' ' * 140) is nil + + # Using the be matcher, write a test that generate_hashtag('The Odin Proejct') is not nil +end diff --git a/ruby_testing/1_rspec_basics/spec/5_contain_exactly_spec.rb b/ruby_testing/1_rspec_basics/spec/5_contain_exactly_spec.rb new file mode 100644 index 0000000000..3a2e4bc37f --- /dev/null +++ b/ruby_testing/1_rspec_basics/spec/5_contain_exactly_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +def create_range(min, max) + (min..max).to_a +end + +RSpec.describe 'Contain Exactly Matcher Exercises' do + # Write a test that create_range(0, 3) contains exactly 0, 1, 2, 3 + + # Write a test that create_range(0, 3) contains exactly 3, 2, 1, 0 + + # Write a test that create_range(0, 3)) contains exactly 1, 3, 0, 2 + + # Write a test that create_range(0, 3) contains exactly 2, 0, 3, 1 +end diff --git a/ruby_testing/1_rspec_basics/spec/spec_helper.rb b/ruby_testing/1_rspec_basics/spec/spec_helper.rb new file mode 100644 index 0000000000..eb06f2502e --- /dev/null +++ b/ruby_testing/1_rspec_basics/spec/spec_helper.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +RSpec.configure do |config| + config.expect_with :rspec do |expectations| + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + config.mock_with :rspec do |mocks| + mocks.verify_partial_doubles = true + end + + config.shared_context_metadata_behavior = :apply_to_host_groups +end + +module FormatterOverrides + def dump_pending(_); end +end + +RSpec::Core::Formatters::DocumentationFormatter.prepend FormatterOverrides diff --git a/ruby_testing/2_rspec_techniques/.rspec b/ruby_testing/2_rspec_techniques/.rspec new file mode 100644 index 0000000000..279bfd9e99 --- /dev/null +++ b/ruby_testing/2_rspec_techniques/.rspec @@ -0,0 +1 @@ +--require spec_helper --format documentation --color diff --git a/ruby_testing/2_rspec_techniques/spec/1_equality_matchers_spec.rb b/ruby_testing/2_rspec_techniques/spec/1_equality_matchers_spec.rb new file mode 100644 index 0000000000..94086dec3a --- /dev/null +++ b/ruby_testing/2_rspec_techniques/spec/1_equality_matchers_spec.rb @@ -0,0 +1,62 @@ +# frozen_string_literal: true + +RSpec.describe 'Equality Matcher Exercises' do + describe 'eq and eql (equality matchers)' do + let(:my_score) { 10.0 } # Float + let(:your_score) { 10 } # Integer + + context 'when using eq matcher' do + # NOTE: the eq matcher looks at the VALUE of the objects. + + # Using the eq matcher, test that my_score is eq to your_score + end + + context 'when using eql matcher' do + # NOTE: the eql matcher looks at the VALUE and the TYPE of the objects. + + # Temporarily remove the 'x' from the following test, run this test, and read the error message. + # It fails because my_score and your_score do not have eql VALUE and TYPE. + xit 'has eql value and type' do + expect(my_score).to eql(your_score) + end + + # Using the eql matcher, test that my_score is not eql to your_score + end + end + + describe 'eql, equal, and be (equality matchers)' do + let(:my_car) { { year: 2013, make: 'Toyota', model: 'Camry' } } + let(:your_car) { { year: 2013, make: 'Toyota', model: 'Camry' } } + let(:my_kids_borrowed_car) { my_car } + + context 'when using eql matcher' do + # Using the eql matcher, test that my_car is eql to your_car + + # Using the eql matcher, test that my_kids_borrowed_car is eql to your_car + end + + # Wait a second... my kids can borrow your car!?! + + context 'when using equal matcher' do + # NOTE: the equal matcher looks at the OBJECT IDENTITY. + + # Temporarily remove the 'x' from the following test, run this test, and read the error message. + # It fails because my_kids_borrowed_car and your_car do not have the same OBJECT IDENTITY. + xit 'has object identity' do + expect(my_kids_borrowed_car).to equal(your_car) + end + + # Using the equal matcher, test that my_kids_borrowed_car is not equal to your_car + end + + # Whew! I bet you're relieved that my kids can not borrow your car anymore! + + context 'when using be matcher' do + # NOTE: the be matcher is similar to the equal matcher (looks at the OBJECT IDENTITY). + + # Using the be matcher, test that my_kids_borrowed_car is my_car + + # Using the be matcher, test that my_kids_borrowed_car is not your_car + end + end +end diff --git a/ruby_testing/2_rspec_techniques/spec/2_lesson_practice_spec.rb b/ruby_testing/2_rspec_techniques/spec/2_lesson_practice_spec.rb new file mode 100644 index 0000000000..38d78dc40d --- /dev/null +++ b/ruby_testing/2_rspec_techniques/spec/2_lesson_practice_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +# In a typical workflow, the class being tested will be located in a separate file from the tests. +# However, for this simple example, we are going to keep everything together. + +class SchoolClub + attr_reader :count, :members + + def initialize(name, members = []) + @name = name + @members = members + @count = members.length + end + + def add_members(*names) + names.each do |name| + @members << name + @count += 1 + end + end + + def remove_members(*names) + names.each do |name| + next unless @members.include?(name) + + @members.delete(name) + @count -= 1 + end + end +end + +RSpec.describe SchoolClub do + # Create a named subject with just a club name, for example SchoolClub.new('Robotics Club') + + describe 'add_members' do + context 'when adding one new member' do + # Using a before block, add one member to the club. For example, robotics_club.add_members('Adam') + + # Test the value of count, for example that robotics_club.count is 1 + + # Test the value of members, for example that robotics_club.members includes Adam + end + + context 'when adding three new members' do + # Using a before block, add three members to the club. + # For example, robotics_club.add_members('Alice', 'Bob', 'Carrie') + + # Test the value of count, for example that robotics_club.count is 3 + + # Test the value of members, for example that robotics_club.members includes Alice, Bob, and Carrie + end + end + + describe 'remove_members' do + # Create a named subject with a club name and a founders variable that we will define in each context. + # For example SchoolClub.new('Chess Club', founders) + + context 'when removing one member from two member club' do + # Create a let variable named founders that is an array with two names, for example, ['Adam', 'Beth'] + + # Using a before block, remove one member from the club. For example, chess_club.remove_members('Beth') + + # Test the value of count, for example that chess_club.count is 1 + + # Test the value of members, for example that chess_club.members does not include Beth + end + + context 'when removing two members from four member club' do + # Create another let variable named founders that is an array with four names. + # For example, ['Alice', 'Bob', 'Carrie', 'Dave'] + + # Using a before block, remove two members from the club. For example, chess_club.remove_members('Carrie', 'Dave') + + # Test the value of count, for example that chess_club.count is 2 + + # Test the value of members, for example that chess_club.members does not include Carrie or Dave + end + end +end diff --git a/ruby_testing/2_rspec_techniques/spec/spec_helper.rb b/ruby_testing/2_rspec_techniques/spec/spec_helper.rb new file mode 100644 index 0000000000..eb06f2502e --- /dev/null +++ b/ruby_testing/2_rspec_techniques/spec/spec_helper.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +RSpec.configure do |config| + config.expect_with :rspec do |expectations| + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + config.mock_with :rspec do |mocks| + mocks.verify_partial_doubles = true + end + + config.shared_context_metadata_behavior = :apply_to_host_groups +end + +module FormatterOverrides + def dump_pending(_); end +end + +RSpec::Core::Formatters::DocumentationFormatter.prepend FormatterOverrides diff --git a/ruby_testing/README.md b/ruby_testing/README.md new file mode 100644 index 0000000000..019b20debe --- /dev/null +++ b/ruby_testing/README.md @@ -0,0 +1,27 @@ +# Ruby Testing Exercises +These exercises are designed to compliment the Ruby Testing lessons from [The Odin Project](https://www.theodinproject.com/). + +### Usage + +1. Navigate to a lesson directory, for example `cd ruby_testing/1_rspec_basics`. +2. Run the tests for the first exercise file, for example `bundle exec rspec spec/1_eq_matcher_spec.rb. **No examples should be found, because it is your turn to write the tests!** +3. Open the exercise spec file in your text editor. +4. Follow the instructions in the file to write **one** test. +5. Run the tests again to verify it passes before moving on to the next test. +6. Repeat steps 4 and 5, until you written all tests in a file. +7. If there are additional exercise spec files in the lesson directory, repeat steps 1-6 for each file. + +### Contents + +#### 1. RSpec - The Basics + +- [ ] Eq Matcher Exercises +- [ ] Include Matcher Exercises +- [ ] Be True/False Matcher Exercises +- [ ] Be Nil Matcher Exercises +- [ ] Contain Exactly Matcher Exercises + +#### 2. RSpec - Hooks, Let and Subject + +- [ ] Equality Matcher Exercises +- [ ] Lesson Practice Exercises From 0abb084d6920fa1031cc5aebe5077cb07b17a38b Mon Sep 17 00:00:00 2001 From: Rachel Moser Date: Sun, 19 Sep 2021 22:15:47 -0500 Subject: [PATCH 2/2] Fix a few minor typos --- ruby_basics/README.md | 2 +- ruby_testing/1_rspec_basics/spec/4_be_nil_matcher_spec.rb | 2 +- ruby_testing/1_rspec_basics/spec/5_contain_exactly_spec.rb | 2 +- ruby_testing/README.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ruby_basics/README.md b/ruby_basics/README.md index 86d72227e4..0b3ee5a477 100644 --- a/ruby_basics/README.md +++ b/ruby_basics/README.md @@ -1,5 +1,5 @@ # Ruby Basics Exercises -These exercises are designed to compliment the [Ruby Basic lessons](https://www.theodinproject.com/courses/ruby-programming#basic-ruby) on [The Odin Project](https://www.theodinproject.com/). Each folder contains exercises and specs(tests) for the lessons in the Ruby Basics section. +These exercises are designed to complement the [Ruby Basic lessons](https://www.theodinproject.com/courses/ruby-programming#basic-ruby) on [The Odin Project](https://www.theodinproject.com/). Each folder contains exercises and specs(tests) for the lessons in the Ruby Basics section. ### Usage diff --git a/ruby_testing/1_rspec_basics/spec/4_be_nil_matcher_spec.rb b/ruby_testing/1_rspec_basics/spec/4_be_nil_matcher_spec.rb index c32df245e5..0c29b9d2ff 100644 --- a/ruby_testing/1_rspec_basics/spec/4_be_nil_matcher_spec.rb +++ b/ruby_testing/1_rspec_basics/spec/4_be_nil_matcher_spec.rb @@ -14,5 +14,5 @@ def generate_hashtag(str) # Using the be matcher, write a test that generate_hashtag(' ' * 140) is nil - # Using the be matcher, write a test that generate_hashtag('The Odin Proejct') is not nil + # Using the be matcher, write a test that generate_hashtag('The Odin Project') is not nil end diff --git a/ruby_testing/1_rspec_basics/spec/5_contain_exactly_spec.rb b/ruby_testing/1_rspec_basics/spec/5_contain_exactly_spec.rb index 3a2e4bc37f..5652b9ae77 100644 --- a/ruby_testing/1_rspec_basics/spec/5_contain_exactly_spec.rb +++ b/ruby_testing/1_rspec_basics/spec/5_contain_exactly_spec.rb @@ -9,7 +9,7 @@ def create_range(min, max) # Write a test that create_range(0, 3) contains exactly 3, 2, 1, 0 - # Write a test that create_range(0, 3)) contains exactly 1, 3, 0, 2 + # Write a test that create_range(0, 3) contains exactly 1, 3, 0, 2 # Write a test that create_range(0, 3) contains exactly 2, 0, 3, 1 end diff --git a/ruby_testing/README.md b/ruby_testing/README.md index 019b20debe..be86c8689a 100644 --- a/ruby_testing/README.md +++ b/ruby_testing/README.md @@ -1,10 +1,10 @@ # Ruby Testing Exercises -These exercises are designed to compliment the Ruby Testing lessons from [The Odin Project](https://www.theodinproject.com/). +These exercises are designed to complement the Ruby Testing lessons from [The Odin Project](https://www.theodinproject.com/). ### Usage 1. Navigate to a lesson directory, for example `cd ruby_testing/1_rspec_basics`. -2. Run the tests for the first exercise file, for example `bundle exec rspec spec/1_eq_matcher_spec.rb. **No examples should be found, because it is your turn to write the tests!** +2. Run the tests for the first exercise file, for example `bundle exec rspec spec/1_eq_matcher_spec.rb`. **No examples should be found, because it is your turn to write the tests!** 3. Open the exercise spec file in your text editor. 4. Follow the instructions in the file to write **one** test. 5. Run the tests again to verify it passes before moving on to the next test.