How-to fix: not find a valid mapping for #<User ...>
Tags: devise, rspec, confirmable, gem
Published: 2020-07-24 20:36:49

Após adicionar a confirmação de usuário em uma Rails gem:
```
class User < ApplicationRecord
...
  devise :confirmable
...
end
```
e atualizar as factories para como padrão ter um `User#confirmed?` 
com `confirmed_at { 1.day.ago }`, os testes que envolviam o model User na minha aplicação passaram a quebrar de uma forma inesperada...

```
Could not find a valid mapping for #<User id: 1...>
```

### Por que os testes quebraram?!

Após ativar o [Devise::Confirmable](https://github.com/heartcombo/devise/blob/master/lib/devise/models/confirmable.rb), o create do objeto passa a chamar o [send_confirmation_instructions](https://github.com/heartcombo/devise/blob/master/lib/devise/models/confirmable.rb#L115).
Esse método por sua vez, depende que na rota do rails esteja configurado *devise_for ou devise_scope :resource*

A gem não tem o arquivo **routes.rb** configurado e com isso, também não tem o mapping `devise_for ou devise_scope` e isso que faz com que o método `Devise::Mapping#find_scope!` *dê raise*
`raise "Could not find a valid mapping for #{obj.inspect}"`

```
Could not find a valid mapping for #<User id: 1...>
     # /Users/username/.rvm/gems/ruby-2.6.6/gems/devise-4.7.2/lib/devise/mapping.rb:46:in
# `find_scope!'
     # /Users/username/.rvm/gems/ruby-2.6.6/gems/devise-4.7.2/lib/devise/mailers/helpers.rb:23:in
     # `initialize_from_record'
     # /Users/username/.rvm/gems/ruby-2.6.6/gems/devise-4.7.2/lib/devise/mailers/helpers.rb:18:in
     # `devise_mail'
     # /Users/username/.rvm/gems/ruby-2.6.6/gems/devise-4.7.2/app/mailers/devise/mailer.rb:9:in
     # `confirmation_instructions'
     # /Users/username/.rvm/gems/ruby-2.6.6/gems/devise-4.7.2/lib/devise/models/authenticatable.rb:200:in
     # `send_devise_notification'
     # /Users/username/.rvm/gems/ruby-2.6.6/gems/devise-4.7.2/lib/devise/models/confirmable.rb:121:in
     # `send_confirmation_instructions'
     # /Users/username/.rvm/gems/ruby-2.6.6/gems/devise-4.7.2/lib/devise/models/confirmable.rb:183:in
     # `send_on_create_confirmation_instructions'
     # ./spec/models/user_spec.rb:125:in `block (4 levels) in <top (required)>''`
```

Deu um pouco de trabalho encontrar essa causa, já que a maioria dos relatos são para projetos completos e não para gems...
Mas uma vez encontrada, a solução foi bem simples ;]

### A solução

No `rails_helper.rb` adicionei as configurações para o Devise não enviar e-mail no ambiente de teste e isso faz com que , não chamar o **Devise::Mapping**

```
RSpec.configure do |config|
...
  Devise::Mailer.delivery_method = :test
  Devise::Mailer.perform_deliveries = false
...
end
```

Na spec que eu precisava testar esse novo comportamento, apenas adicionei um mock para o método que envia o email:


```
...
allow_any_instance_of(described_class)
  .to receive(:send_confirmation_instructions)
...
```

### TL; DR
O Devise chama o disparo de e-mail apenas quando o `confirmed_at` é do momento corrente (`Time.current` e suas variações)
Inicialmente, antes de encontrar a configuração do rails_helper, havia alterado o `confirmed_at { 1.day.ago }` para `confirmed_at { 1.day.ago }` e havia resolvido, daí a descoberta do momento de disparo do e-mail =]


*Não confie em mim , leia a doc :)*
[DOC Confirmable](https://rubydoc.info/github/plataformatec/devise/master/Devise/Models/Confirmable)
[DOC Devise::Mapping](https://github.com/heartcombo/devise/blob/master/lib/devise/mapping.rb)