Как писать тесты с Minitest или RSpec?ruby-50

1. Minitest

Базовая структура теста

require 'test_helper'

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

  test "email should be unique" do
    existing_user = users(:one)
    new_user = User.new(email: existing_user.email)
    assert_not new_user.valid?
  end
end

Основные методы:

  • assert / assert_not - базовые проверки
  • assert_equal - проверка равенства
  • assert_difference - проверка изменения значения
  • assert_raises - проверка исключений

Системные тесты:

require 'application_system_test_case'

class UsersTest < ApplicationSystemTest
  test "visiting the index" do
    visit users_url
    assert_selector "h1", text: "Users"
  end
end

2. RSpec

Установка:

gem 'rspec-rails'
rails generate rspec:install

Модельные тесты :

describe User do
  let(:user) { build(:user) }

  it "is valid with valid attributes" do
    expect(user).to be_valid
  end

  context "without email" do
    before { user.email = nil }
    it { should_not be_valid }
  end
end

Контроллер-тесты:

describe UsersController do
  describe "GET #index" do
    it "returns http success" do
      get :index
      expect(response).to have_http_status(:success)
    end

    it "assigns @users" do
      user = create(:user)
      get :index
      expect(assigns(:users)).to eq([user])
    end
  end
end

Системные тесты :

require 'rails_helper'

RSpec.feature "Users", type: :system do
  scenario "User creates new account" do
    visit new_user_path
    fill_in "Email", with: "test@example.com"
    click_button "Create"
    expect(page).to have_text("Account created")
  end
end

Ключевые различия

Minitest:

  • Встроен в Rails
  • Простой синтаксис (на основе assert)
  • Быстрее работает
  • Меньше "магии"

RSpec:

  • Более выразительный DSL
  • Поддержка context/describe
  • Лучше для BDD
  • Больше возможностей для мокирования

Общие лучшие практики

  1. Именование тестов:

    • Minitest: test "description" do
    • RSpec: it "does something" do
  2. Фабрики вместо фикстур:

    # Используйте FactoryBot
    let(:user) { create(:user, email: "test@example.com") }
    
  3. Принцип 3A:

    • Arrange (подготовка)
    • Act (действие)
    • Assert (проверка)
  4. Тестируйте поведение, а не реализацию

  5. Добавляйте контексты для разных случаев:

    context "when user is admin" do
      before { user.update(admin: true) }
      it { should be_able_to :manage, :all }
    end
    

Продвинутые техники

Моки и стабы :

it "sends welcome email" do
  allow(UserMailer).to receive(:welcome).and_return(double(deliver_now: true))
  user = create(:user)
  expect(UserMailer).to have_received(:welcome).with(user)
end

Тесты фоновых задач:

it "enqueues job" do
  expect {
    User.create(email: "test@example.com")
  }.to have_enqueued_job(SendWelcomeEmailJob)
end

Тестирование API:

it "returns JSON" do
  get "/api/users", as: :json
  expect(response).to have_http_status(:ok)
  expect(JSON.parse(response.body)).to include("users")
end

Резюмируем: и Minitest, и RSpec позволяют эффективно тестировать Rails-приложения. Minitest проще и быстрее, RSpec предлагает более выразительный синтаксис для сложных сценариев. Выбор зависит от предпочтений команды и сложности проекта. Главное — писать чистые, изолированные тесты, которые действительно проверяют функциональность приложения.