Pruebas con RSpec
## Objetivos de Aprendizaje
- Comprender los fundamentos de pruebas
- Escribir pruebas RSpec
- Usar matchers efectivamente
- Organizar pruebas con describe y context
## Fundamentos de Pruebas
### Por que Probar?
- Detectar bugs tempranamente
- Prevenir regresiones
- Documentar comportamiento
- Habilitar refactorizacion con confianza
### Tipos de Pruebas
| Tipo | Descripcion |
|------|-------------|
| Unitaria | Probar metodos individuales |
| Integracion | Probar interacciones entre componentes |
| Funcional | Probar caracteristicas de extremo a extremo |
| Rendimiento | Probar velocidad y escalabilidad |
## Vision General de RSpec
RSpec es el framework BDD de pruebas mas popular de Ruby.
### Instalacion
```ruby
# Gemfile
group :test do
gem 'rspec'
end
```
```bash
bundle install
rspec --init
```
## Estructura Basica
### Archivo Spec
```ruby
# spec/calculadora_spec.rb
RSpec.describe Calculadora do
describe '#sumar' do
it 'retorna la suma de dos numeros' do
calc = Calculadora.new
expect(calc.sumar(2, 3)).to eq(5)
end
end
end
```
### Ejecutar Pruebas
```bash
rspec
rspec spec/calculadora_spec.rb
rspec spec/calculadora_spec.rb:10 # Ejecutar prueba especifica
```
## Describe y Context
```ruby
RSpec.describe Pedido do
describe '#total' do
context 'cuando el pedido esta vacio' do
it 'retorna cero' do
pedido = Pedido.new
expect(pedido.total).to eq(0)
end
end
context 'cuando el pedido tiene articulos' do
it 'retorna la suma de precios' do
pedido = Pedido.new
pedido.agregar_articulo(Articulo.new(precio: 10))
pedido.agregar_articulo(Articulo.new(precio: 20))
expect(pedido.total).to eq(30)
end
end
end
end
```
## Matchers
### Igualdad
```ruby
expect(actual).to eq(esperado) # Valor igual
expect(actual).to eql(esperado) # Valor igual (menos estricto)
expect(actual).to be(esperado) # Mismo objeto
expect(actual).to equal(esperado) # Mismo objeto (identidad)
```
### Veracidad
```ruby
expect(actual).to be_truthy
expect(actual).to be_falsey
expect(actual).to be_nil
expect(actual).to be true
expect(actual).to be false
```
### Comparaciones
```ruby
expect(actual).to be > valor
expect(actual).to be >= valor
expect(actual).to be < valor
expect(actual).to be <= valor
expect(actual).to be_between(min, max)
expect(actual).to match(/patron/)
```
### Colecciones
```ruby
expect(actual).to include(elemento)
expect(actual).to contain_exactly(elemento1, elemento2)
expect(actual).to start_with(item)
expect(actual).to end_with(item)
expect(actual).to have_attributes(clave: valor)
```
### Tipos
```ruby
expect(actual).to be_a(Clase)
expect(actual).to be_an_instance_of(Clase)
expect(actual).to respond_to(:nombre_metodo)
```
### Errores
```ruby
expect { codigo }.to raise_error
expect { codigo }.to raise_error(ClaseDeError)
expect { codigo }.to raise_error('mensaje')
expect { codigo }.to raise_error(ClaseDeError, 'mensaje')
```
### Predicados
```ruby
expect(actual).to be_empty
expect(actual).to be_valid
expect(array).to be_an(Array)
```
## Hooks Before y After
```ruby
RSpec.describe Usuario do
before(:each) do
@usuario = Usuario.create(nombre: 'Alicia')
end
after(:each) do
Usuario.destroy_all
end
before(:all) do
@admin = Usuario.create(nombre: 'Admin', rol: 'admin')
end
after(:all) do
Usuario.destroy_all
end
# O con :context para configuracion por contexto
before(:context) do
# Se ejecuta una vez antes de todos los ejemplos en este grupo
end
end
```
## Let y Let
```ruby
RSpec.describe Usuario do
# lazy evaluation (solo creado cuando se usa)
let(:usuario) { Usuario.create(nombre: 'Alicia') }
# eager evaluation (creado antes de cada ejemplo)
let!(:post) { Post.create(titulo: 'Prueba', autor: 'Alicia') }
it 'tiene posts' do
expect(usuario.posts).to include(post)
end
end
```
## Ejemplos Compartidos
```ruby
RSpec.shared_examples 'una cosa contable' do
it 'comienza en cero' do
expect(subject.count).to eq(0)
end
it 'incrementa el conteo' do
subject.increment
expect(subject.count).to eq(1)
end
end
RSpec.describe Contador do
it_behaves_like 'una cosa contable'
end
RSpec.describe Lista do
it_behaves_like 'una cosa contable'
end
```
## Focus y Skip
```ruby
# Focus - ejecutar solo esta prueba
it 'esta prueba', focus: true do
expect(true).to be true
end
# Skip - no ejecutar esta prueba
it 'no se ejecutara', skip: true do
expect(true).to be false
end
# Pending - se ejecuta pero no falla
it 'esta pendiente' do
skip 'razon'
end
```
## Test Doubles
### Dummy
```ruby
let(:dummy) { Object.new }
```
### Fake
```ruby
fake_usuario_repo = FakeUsuarioRepositorio.new
```
### Stub
```ruby
allow(Object).to receive(:metodo).and_return(valor)
allow(Object).to receive_messages(metodo1: valor1, metodo2: valor2)
```
### Mock
```ruby
expect(Object).to receive(:metodo).with(args).and_return(valor)
expect(Object).to receive(:metodo).exactly(3).times
expect(Object).to receive(:metodo).at_least(:once)
```
### Spy
```ruby
Object spy: Object.new
allow(Object).to receive(:metodo)
Object.metodo
expect(Object).to have_received(:metodo)
```
## Feature Specs (Capybara)
```ruby
require 'capybara/rspec'
RSpec.describe 'Registro de usuario' do
scenario 'usuario se registra exitosamente' do
visit '/usuarios/nuevo'
fill_in 'Nombre', with: 'Alicia'
fill_in 'Email', with: 'alice@ejemplo.com'
click_button 'Registrarse'
expect(page).to have_content('Bienvenida, Alicia!')
end
end
```
## Organizacion de Pruebas
```text
spec/
├── spec_helper.rb # Configuracion compartida
├── support/ # Modulos de ayuda
│ └── factories.rb
├── models/
│ ├── usuario_spec.rb
│ └── post_spec.rb
└── features/
└── registro_usuario_spec.rb
```
### spec_helper.rb
```ruby
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
config.filter_run_when_matching :focus
config.example_status_persistence_file_path = 'spec/examples.txt'
config.disable_monkey_patching!
config.order = :random
Kernel.srand config.seed
end
```
## Resumen
- RSpec usa `describe` y `context` para agrupar pruebas
- `it` define un caso de prueba
- Matchers: `eq`, `be`, `include`, `match`, `raise_error`, etc.
- Hooks `before`/`after` para configuracion/desmontaje
- `let` y `let!` para datos de prueba
- Test doubles: dummy, fake, stub, mock, spy
- Ejemplos compartidos para logica de prueba reutilizable
- `rspec --init` para inicializar RSpec
Comments
Comments powered by Giscus
To enable comments, add your Giscus embed code here.
Learn more about Giscus →