We use minitest as a testing framework for Ruby coding exercises.
Before we go into details, let's see how you can use minitest to test the most basic Ruby task: writing a function that returns "Hello, World!".
This is what a student might write (hello.rb):
def hello_message"Hello, World!"end
And this is an example of what you could write to evaluate the student's code:
require "minitest/autorun"require "./hello"​class Evaluate < Minitest::Testdef test_hello_messageassert_equal "Hello, World!",hello_message,"Your function should return: Hello, World!"endend
Let's examine the above evaluation code.
We first need to require the minitest framework and the user file(s).
Then we need to define a suite of unit tests as a class that inherits from Minitest::Test
.
This groups together multiple unit tests and allows implementing common functionality that you want executed before and after each test, via the setup()
and teardown()
methods respectively.
Unit tests can be implemented as methods inside this class that start with test_
. These are the only functions that will be executed when running the evaluation code.
class Evaluate < Minitest::Testdef setup# This code will be executed before every unit test.end​def teardown# This code will be executed after every unit test.end​def test_something# This is a unit test.end​def test_sometings_else# This is another unit test.endend
​Assertions are the statements that actually verify that the student's code behaves as expected. The most common pattern is to call student's methods and assert that their return value is the one that you expect. If it is not, then the test will fail and you have the opportunity to provide a custom message to the student for each failed assertion, so that she can improve.
These are the most common ones:
assert(boolean_value, msg = nil)assert_equal(expected_value, actual_value, msg = nil)
The assertion from the Hello World example could be rewritten as:
assert "Hello, World!" == hello_message,"Your function should return: Hello, World!"
If any of the assertions in your unit tests fail, the test is marked as failed and feedback (the msg
parameter) is given to the student.
In the previous example we had the students return Hello, World!
, but actually a more common task is to have them print the same string to the standard output.
def say_helloputs "Hello, World!"end
How do we test this specific function?
require "minitest/autorun"require "./hello"​class Evaluate < Minitest::Testdef test_hello_messageout, err = capture_io dosay_helloend​assert_equal "Hello, World!\n", out, "Your function should return: Hello, World!"endend
Minitest provides the capture_io method which captures and returns standard output and standard error for all the statements that are executed within the method's block.
Ruby >= 2.3.1
Minitest >= 5.9.0
Given:
An integer number n
Your task is to:
Write a function that returns an array with the numbers from 1 to n with the following restrictions:
for multiples of 3 store "Fizz" instead of the number
for multiples of 5 store "Buzz" instead of the number
for numbers which are multiples of both 3 and 5 store "FizzBuzz"
Example:
fizzbuzz(15) == [1, 2, "Fizz", 4, "Buzz","Fizz", 7, 8, "Fizz", "Buzz",11, "Fizz", 13, 14, "FizzBuzz"]
Here's how a student might solve the problem:
def fizzbuzz(n)result = []​(1..n).each do |i|if i % 15 == 0result.push "FizzBuzz"elsif i % 3 == 0result.push "Fizz"elsif i % 5 == 0result.push "Buzz"elseresult.push iendend​resultend
Here's how you might test the student's solution:
require "minitest/autorun"require "./fizzbuzz"​class Evaluate < Minitest::Testdef test_fizzbuzz_simpleassert_equal [1], fizzbuzz(1), "fizzbuzz(1) should return [1]"end​def test_fizzbuzzresult = fizzbuzz(15)​assert result.kind_of?(Array), "Your function should return an array"​expected = [1, 2, "Fizz", 4, "Buzz","Fizz", 7, 8, "Fizz", "Buzz",11, "Fizz", 13, 14, "FizzBuzz"]​assert_equal expected.length,result.length,"Your function should return #{expected.length} objects"​result.each_with_index do |obj, idx|assert_equal expected[idx],obj,"Your function should map #{idx + 1} to #{expected[idx]} instead of #{obj}"endendend
Given:
A sentence with words separated by spaces
Your task is to:
Write a function that returns a copy of this sentence where all the words:
start with a capital letter
the rest of the letters are lower case
Note:
Your function should not modify the sentence given as argument.
Example:
normalize("This is SO MUCH FUN! ") == "This Is So Much Fun! "
Here's how a student might solve the problem:
def normalize(sentence)copy = sentence.dupidx = 0​while idx < copy.lengthidx = skip_whitespace copy, idxidx = normalize_word! copy, idxend​copyend​def skip_whitespace(sentence, idx)while idx < sentence.length and sentence[idx] == ' 'idx += 1end​idxend​def normalize_word!(sentence, idx)if idx < sentence.length and sentence[idx] != ' 'sentence[idx] = sentence[idx].upcaseidx += 1end​while idx < sentence.length and sentence[idx] != ' 'sentence[idx] = sentence[idx].downcaseidx += 1end​idxend
Here's how you might test the student's solution:
require "minitest/autorun"require "./normalize"​class Evaluate < Minitest::Testdef test_characterassert_equal "A", normalize("a"), "Your function should convert a single character"assert_equal "A", normalize("A"), "Your function should convert a single character"end​def test_no_mutatesentence = "a"normalize sentenceassert_equal "a", sentence, "Your function should not mutate the argument"end​def test_wordassert_equal "Abc", normalize("abc"), "Your function should convert a single word"assert_equal "Abc", normalize("ABC"), "Your function should convert a single word"assert_equal "Abc", normalize("aBC"), "Your function should convert a single word"assert_equal "Abc", normalize("Abc"), "Your function should convert a single word"end​def test_sentenceassert_equal "Abc Def Ghi",normalize("abc def ghi"),"Your function should convert a sentence"end​def test_multiple_whitespaceassert_equal " Abc Def Ghi ",normalize(" abc def ghi "),"Your function should keep multiple whitespaces"endend