Como testar uma preocupação no Rails


104

Visto que tenho uma Personablepreocupação em minha aplicação Rails 4 que tem um full_namemétodo, como eu faria para testar isso usando RSpec?

preocupações / personable.rb

module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end

Qual estrutura de teste você está usando? Lembre-se também de que o Personable é apenas um módulo Ruby normal. Teste-o exatamente como faria com qualquer outro mixin.
Lee Jarvis

Não ActiveSupport::Concernfoi retirado do Rails? Achei que já tivesse ido há pouco.
Russell

@LeeJarvis Estou usando Rspec junto com FactoryGirl
Kyle Decot


4
@Russell eu concordo. Dito isso, eu não ajudaria ninguém com suas perguntas só porque eles estavam seguindo uma maneira do Rails de fazer algo com a qual eu não concordo. De qualquer forma, isso é meio que fugir do assunto desta questão :-)
Lee Jarvis

Respostas:


176

O método que você encontrou certamente funcionará para testar um pouco de funcionalidade, mas parece muito frágil - sua classe fictícia (na verdade, apenas a Structem sua solução) pode ou não se comportar como uma classe real que includeé a sua preocupação. Além disso, se você estiver tentando testar as preocupações do modelo, não será capaz de fazer coisas como testar a validade de objetos ou invocar retornos de chamada ActiveRecord a menos que configure o banco de dados de acordo (porque sua classe fictícia não terá um suporte de tabela de banco de dados isto). Além disso, você desejará não apenas testar a preocupação, mas também testar o comportamento da preocupação dentro das especificações do seu modelo.

Então, por que não matar dois coelhos com uma cajadada só? Usando os grupos de exemplos compartilhados do RSpec , você pode testar suas preocupações em relação às classes reais que os usam (por exemplo, modelos) e poderá testá-los em todos os lugares em que são usados. E você só precisa escrever os testes uma vez e depois incluí-los em qualquer especificação de modelo que use sua preocupação. No seu caso, pode ser algo assim:

# app/models/concerns/personable.rb
module Personable
  extend ActiveSupport::Concern

  def full_name
    "#{first_name} #{last_name}"
  end
end

# spec/concerns/personable_spec.rb
require 'spec_helper'

shared_examples_for "personable" do
  let(:model) { described_class } # the class that includes the concern

  it "has a full name" do
    person = FactoryBot.build(model.to_s.underscore.to_sym, first_name: "Stewart", last_name: "Home")
    expect(person.full_name).to eq("Stewart Home")
  end
end

# spec/models/master_spec.rb
require 'spec_helper'
require Rails.root.join "spec/concerns/personable_spec.rb"

describe Master do
  it_behaves_like "personable"
end

# spec/models/apprentice_spec.rb
require 'spec_helper'

describe Apprentice do
  it_behaves_like "personable"
end

As vantagens dessa abordagem se tornam ainda mais óbvias quando você começa a fazer coisas em sua preocupação, como invocar callbacks de AR, onde qualquer coisa menos do que um objeto AR simplesmente não funcionará.


2
Uma desvantagem disso é que ele ficará mais lento parallel_tests. Acho que será melhor ter testes separados em vez de usar shared_examples_fore it_behaves_like.
Artem Kalinchuk de

6
@ArtemKalinchuk Não tenho certeza se isso é verdade, por github.com/grosser/parallel_tests/issues/168 parallel_tests são baseados em arquivo, portanto, exemplos compartilhados não devem retardá-lo. Eu também argumentaria que comportamentos compartilhados adequadamente agrupados superam a velocidade do teste.
Aaron K,

8
Certifique-se de incluir o concernsdiretório em seu spec_helper.rb github.com/rspec/rspec-core/issues/407#issuecomment-1409871
Ziggy

1
Não consegui encontrar nada sobre como incluir o diretório de preocupações nesse link. Você poderia esclarecer como isso é feito? Não consigo fazer meu teste RSpec reconhecer o módulo em uma das minhas preocupações.
Jake Smith

4
Não adicione _specao nome do arquivo que contém shared_examples_for (personable_spec.rb neste caso), caso contrário, você receberá uma mensagem de aviso enganosa - github.com/rspec/rspec-core/issues/828 .
Lalu

62

Em resposta aos comentários que recebi, eis o que acabei fazendo (se alguém tiver melhorias, sinta-se à vontade para publicá-las) :

especificações / preocupações / personable_spec.rb

require 'spec_helper'

describe Personable do
  let(:test_class) { Struct.new(:first_name, :last_name) { include Personable } }
  let(:personable) { test_class.new("Stewart", "Home") }

  it "has a full_name" do
    expect(personable.full_name).to eq("#{personable.first_name} #{personable.last_name}")
  end
end

1
Sim, isso interromperá outros testes se eles testarem uma classe real chamada Person. Vou editar para corrigir.
Russell

Isso não funciona. Isso me dá o erro:undefined method 'full_name' for #<struct first_name="Stewart", last_name="Home">
Kyle Decot

Tente incluir Personable em vez de estendê-lo. Vou atualizar a resposta.
Russell

Funciona muito bem agora. Obrigado por me apontar na direção certa e me ajudar a refatorar @Russell
Kyle Decot

Funciona muito bem e tem uma boa aparência
Edward

7

Outra idéia é usar a gem with_model para testar coisas como essa. Eu estava procurando testar uma preocupação e vi a gema pg_search fazendo isso . Parece muito melhor do que testar em modelos individuais, uma vez que eles podem mudar, e é bom definir o que você vai precisar em sua especificação.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.