Testing with Minitest

What is Minitest?

Minitest is Ruby's built-in testing framework. It's lightweight, fast, and comes with Ruby by default.

Setting Up Minitest

Basic Setup

# test_helper.rb
require 'minitest/autorun'

# Your test files will require this

RSpec-style Syntax (Optional)

require 'minitest/autorun'
require 'minitest/spec'

describe "MyClass" do
  # Tests go here
end

Writing Tests

Unit Tests

require 'minitest/autorun'
require_relative '../lib/calculator'

class CalculatorTest < Minitest::Test
  def setup
    @calc = Calculator.new
  end

  def test_addition
    result = @calc.add(2, 3)
    assert_equal 5, result
  end

  def test_subtraction
    result = @calc.subtract(5, 3)
    assert_equal 2, result
  end

  def test_division_by_zero
    assert_raises(ZeroDivisionError) do
      @calc.divide(10, 0)
    end
  end
end

Assertions

# Equality
assert_equal expected, actual
assert_not_equal expected, actual

# Truthiness
assert truthy_value
assert_not falsey_value

# Nil checks
assert_nil value
assert_not_nil value

# Instance checks
assert_instance_of String, value
assert_kind_of Numeric, value

# Matching
assert_match /regex/, string
assert_no_match /regex/, string

# Collections
assert_includes [1, 2, 3], 2
assert_empty []
assert_not_empty [1]

# Exceptions
assert_raises ZeroDivisionError do
  1 / 0
end

# Custom messages
assert_equal 5, 2 + 2, "Math is broken"

Test Structure

Setup and Teardown

class UserTest < Minitest::Test
  def setup
    @user = User.new("John", 30)
  end

  def teardown
    # Cleanup code
    @user = nil
  end

  # Tests...
end

Class-level Setup

class DatabaseTest < Minitest::Test
  def self.setup
    # Run once before all tests in class
    create_test_database
  end

  def self.teardown
    # Run once after all tests in class
    drop_test_database
  end
end

Running Tests

Basic Execution

# Run all tests
ruby -m minitest test/**/*.rb

# Run specific test file
ruby test/calculator_test.rb

# Run specific test method
ruby -m minitest test/calculator_test.rb -n test_addition

Rake Integration

# Rakefile
require 'rake/testtask'

Rake::TestTask.new do |t|
  t.libs << "test"
  t.test_files = FileList['test/**/*_test.rb']
  t.verbose = true
end

task default: :test
rake test

Mocks and Stubs

Using Minitest::Mock

require 'minitest/autorun'

class OrderTest < Minitest::Test
  def test_process_payment
    payment_service = Minitest::Mock.new
    payment_service.expect(:charge, true, [100, "USD"])

    order = Order.new(payment_service)
    result = order.process_payment(100, "USD")

    assert result
    payment_service.verify
  end
end

Stubbing Methods

class UserTest < Minitest::Test
  def test_send_welcome_email
    user = User.new("john@example.com")

    # Stub the mailer method
    user.stub :send_email, "email_sent" do
      result = user.send_welcome_email
      assert_equal "email_sent", result
    end
  end
end

Spec-style Testing

require 'minitest/autorun'
require 'minitest/spec'

describe Calculator do
  before do
    @calc = Calculator.new
  end

  describe "#add" do
    it "adds two numbers" do
      @calc.add(2, 3).must_equal 5
    end

    it "handles negative numbers" do
      @calc.add(-1, 1).must_equal 0
    end
  end

  describe "#divide" do
    it "raises error when dividing by zero" do
      proc { @calc.divide(10, 0) }.must_raise ZeroDivisionError
    end
  end
end

Benchmarking

require 'minitest/autorun'
require 'minitest/benchmark'

class BenchmarkTest < Minitest::Benchmark
  def bench_my_algorithm
    assert_performance_linear 0.99 do |n|
      # Code to benchmark
      n.times do
        my_algorithm
      end
    end
  end
end

Custom Assertions

module CustomAssertions
  def assert_valid_email(email)
    assert_match /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i, email,
                 "#{email} is not a valid email"
  end
end

class Minitest::Test
  include CustomAssertions
end

# Now you can use it in tests
class UserTest < Minitest::Test
  def test_email_validation
    user = User.new("invalid-email")
    assert_valid_email user.email
  end
end

Test Organization

Directory Structure

test/
├── test_helper.rb
├── unit/
│   ├── calculator_test.rb
│   └── user_test.rb
├── integration/
│   └── user_registration_test.rb
└── fixtures/
    └── users.yml

Fixtures

# test/fixtures/users.yml
john:
  name: John Doe
  email: john@example.com

jane:
  name: Jane Smith
  email: jane@example.com
class UserTest < Minitest::Test
  def setup
    @users = YAML.load_file('test/fixtures/users.yml')
  end
end

Best Practices

Integration with Rails

If using Rails, Minitest is the default testing framework:

# test/models/user_test.rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  test "should not save user without email" do
    user = User.new
    assert_not user.save
  end
end

Minitest provides a solid foundation for testing Ruby applications. Its simplicity and speed make it an excellent choice for both small scripts and large applications. Focus on writing clear, maintainable tests that give you confidence in your code.

Loading