Estou testando um modelo com um retorno de chamada after create que gostaria de executar apenas em algumas ocasiões durante o teste. Como posso pular / executar callbacks de uma fábrica?
class User < ActiveRecord::Base
after_create :run_something
...
end
Fábrica:
FactoryGirl.define do
factory :user do
first_name "Luiz"
last_name "Branco"
...
# skip callback
factory :with_run_something do
# run callback
end
end
ruby-on-rails
rspec
factory-bot
luizbranco
fonte
fonte
:on => :create
validação, useafter(:build) { |user| user.class.skip_callback(:validate, :create, :after, :run_something) }
Class.skip_callback
chamada será persistente em outros testes, portanto, se seus outros testes esperam que o retorno de chamada ocorra, eles falharão se você tentar inverter a lógica de retorno de chamada ignorada.after(:build)
bloco. Isso permite que seu padrão de fábrica execute o retorno de chamada e não requer a redefinição do retorno de chamada após cada uso.Quando você não quiser executar um retorno de chamada, faça o seguinte:
Esteja ciente de que skip_callback será persistente em outras especificações após ser executado, portanto, considere algo como o seguinte:
fonte
Nenhuma dessas soluções é boa. Eles desfiguram a classe removendo funcionalidade que deve ser removida da instância, não da classe.
Em vez de suprimir o retorno de chamada, estou suprimindo a funcionalidade do retorno de chamada. De certa forma, gosto mais dessa abordagem porque é mais explícita.
fonte
around_*
(por exemplouser.define_singleton_method(:around_callback_method){|&b| b.call }
).Eu gostaria de melhorar a resposta de @luizbranco para tornar o callback after_save mais reutilizável ao criar outros usuários.
Executando sem callback after_save:
Executando com callback after_save:
Em meu teste, prefiro criar usuários sem o retorno de chamada por padrão, porque os métodos usados executam coisas extras que normalmente não quero em meus exemplos de teste.
---------- ATUALIZAÇÃO ------------ Eu parei de usar skip_callback porque havia alguns problemas de inconsistência no conjunto de testes.
Solução alternativa 1 (uso de stub e unstub):
Solução alternativa 2 (minha abordagem preferida):
fonte
Rails 5 -
skip_callback
aumentando o erro de argumento ao pular de uma fábrica de FactoryBot.Houve uma mudança no Rails 5 com a forma como skip_callback lida com callbacks não reconhecidos:
Quando
skip_callback
é chamado da fábrica, o retorno de chamada real no modelo AR ainda não está definido.Se você já tentou de tudo e puxou o cabelo como eu, aqui está sua solução (consegui pesquisando problemas do FactoryBot) ( NOTE a
raise: false
parte ):Sinta-se à vontade para usá-lo com quaisquer outras estratégias de sua preferência.
fonte
Esta solução funciona para mim e você não precisa adicionar um bloco adicional à sua definição de fábrica:
fonte
Um esboço simples funcionou melhor para mim no Rspec 3
fonte
User
;:run_something
não é um método de classe.Nota importante que você deve especificar ambos. Se usar antes e executar várias especificações, ele tentará desabilitar o retorno de chamada várias vezes. Terá sucesso na primeira vez, mas na segunda, o retorno de chamada não será mais definido. Então vai dar um erro
fonte
Ligar para skip_callback da minha fábrica foi problemático para mim.
No meu caso, tenho uma classe de documento com alguns retornos de chamada relacionados ao s3 antes e depois da criação que só quero executar quando for necessário testar a pilha completa. Caso contrário, quero pular esses retornos de chamada s3.
Quando tentei skip_callbacks em minha fábrica, persistiu esse salto de retorno de chamada mesmo quando eu criei um objeto de documento diretamente, sem usar uma fábrica. Em vez disso, usei stubs de mocha na chamada de pós-compilação e tudo está funcionando perfeitamente:
fonte
before_validation
gancho (tentando fazerskip_callback
com qualquer um dos FactoryGirlsbefore
ouafter
opções parabuild
ecreate
não funcionou)Isso funcionará com a sintaxe rspec atual (a partir desta postagem) e é muito mais limpo:
fonte
A resposta de James Chevalier sobre como pular o retorno de chamada before_validation não me ajudou, então se você se esforçar da mesma forma que eu aqui está a solução de trabalho:
no modelo:
na fábrica:
fonte
Model.skip_callback(...)
No meu caso, o retorno de chamada está carregando algo no cache do redis. Mas eu não tinha / queria uma instância do Redis em execução no meu ambiente de teste.
Para minha situação, semelhante à acima, acabei de
load_to_cache
criar um esboço do meu método em meu spec_helper, com:Além disso, em certas situações em que quero testar isso, só preciso descompactá-los no bloco anterior dos casos de teste Rspec correspondentes.
Eu sei que você pode ter algo mais complicado acontecendo no seu
after_create
ou pode não achar isso muito elegante. Você pode tentar cancelar o callback definido em seu modelo, definindo umafter_create
gancho em seu Factory (consulte a documentação da factory_girl), onde você provavelmente pode definir o mesmo callback e retornofalse
, de acordo com a seção 'Cancelando callbacks' deste artigo . (Não tenho certeza sobre a ordem em que o retorno de chamada é executado, por isso não escolhi essa opção).Por último, (desculpe, não consegui encontrar o artigo) Ruby permite que você use alguma meta-programação suja para liberar um gancho de retorno de chamada (você terá que reiniciá-lo). Eu acho que essa seria a opção menos preferida.
Bem, há mais uma coisa, não é realmente uma solução, mas veja se você consegue se safar com Factory.build em suas especificações, ao invés de realmente criar o objeto. (Seria o mais simples se você pudesse).
fonte
Com relação à resposta postada acima, https://stackoverflow.com/a/35562805/2001785 , você não precisa adicionar o código de fábrica. Achei mais fácil sobrecarregar os métodos nas próprias especificações. Por exemplo, em vez de (em conjunto com o código de fábrica na postagem citada)
Eu gosto de usar (sem o código de fábrica citado)
Dessa forma, você não precisa examinar os arquivos de fábrica e de teste para entender o comportamento do teste.
fonte
Descobri que a solução a seguir é uma maneira mais limpa, já que o retorno de chamada é executado / definido no nível da classe.
fonte
Aqui está um snippet que criei para lidar com isso de uma forma genérica.
Ele irá pular todos os callbacks configurados, incluindo callbacks relacionados ao rails
before_save_collection_association
, mas não vai pular alguns necessários para fazer o ActiveRecord funcionar bem, comoautosave_associated_records_for_
callbacks gerados automaticamente .então mais tarde:
Desnecessário dizer, YMMV, então dê uma olhada nos logs de teste o que você realmente está pulando. Talvez você tenha uma joia adicionando um retorno de chamada que você realmente precisa e isso fará com que seus testes falhem miseravelmente ou de seu modelo de gordura de 100 retornos de chamada, você só precisa de alguns para um teste específico. Para esses casos, tente o temporário
:force_callbacks
BÔNUS
Às vezes, você também precisa pular as validações (tudo em um esforço para tornar os testes mais rápidos) e, em seguida, tente:
fonte
Você pode apenas definir o retorno de chamada com uma característica para aquelas instâncias quando quiser executá-lo.
fonte