Como uso a Factory Girl para gerar um anexo de clipe de papel?

119

Eu tenho o modelo Person que possui muitas imagens, em que as imagens têm um campo de anexo de clipe de papel chamado data, uma versão abreviada exibida abaixo:

class Person
  has_many :images
  ...
end

class Image
  has_attached_file :data
  belongs_to :person
  ...
end

É necessário que a pessoa tenha pelo menos uma imagem anexada.

Ao usar o FactoryGirl, tenho um código semelhante ao seguinte:

Factory.define :image do |a|
  a.data { File.new(File.join(Rails.root, 'features', 'support', 'file.png')) }
  a.association :person
end

Factory.define :person do |p|
  p.first_name 'Keyzer'
  p.last_name 'Soze'
  p.after_create do |person|
    person.assets = [Factory.build(:image, :person => person)]
  end
  # p.images {|images| [images.association(:image)]}
end

(NB: Eu também tentei o código comentado acima também foi tentado) Na maioria das vezes, quando executo recursos de pepino, recebo um erro semelhante ao seguinte:

Esse arquivo ou diretório não existe - /tmp/stream.9887,0.png (Errno :: ENOENT)

...

Às vezes, os testes são executados com sucesso.

Alguém pode me dizer qual é o problema que estou tendo aqui ou como eles usam o FactoryGirl e o Paperclip juntos para obter algo parecido com o que estou tentando alcançar?

Estou usando o Rails 3.

Laz
fonte
2
Voto em questão apenas por usar o Keyzer Soze ao testar o nome!
Breno
Quando faço isso ... as fotos são geradas e colocadas na pasta public / system toda vez que executo o conjunto de testes. As fotos não são destruídas após a execução da suíte de testes. Alguém mais notou isso?
precisa saber é o seguinte

Respostas:

86

Você pode usar fixture_file_upload

include ActionDispatch::TestProcess no seu auxiliar de teste, aqui está um exemplo de fábrica:

include ActionDispatch::TestProcess

FactoryBot.define do
  factory :user do
    avatar { fixture_file_upload(Rails.root.join('spec', 'photos', 'test.png'), 'image/png') }
  end
end

No exemplo acima, spec/photos/test.pngprecisa existir no diretório raiz do seu aplicativo antes de executar seus testes.

Observe que esse FactoryBoté um novo nome para FactoryGirl.

DanS
fonte
2
Esta resposta está totalmente correta, mas pode ser mais claro dizer que a inclusão do TestProcess precisa ser incluída no Factory e não apenas no seu spec_helper.
Andrew Hubbs
2
Obrigado pelo esclarecimento, Andrew. Você também pode precisar colocar "include ActionDispatch :: TestProcess" antes do bloco FactoryGirl.define (que não é o que acontece nesta lista principal: gist.github.com/313121 ).
Sam
3
Além disso, { }são necessários. Ele quebra sem ele
bigpotato
3
Esclarecimentos adicionais: include ActionDispatch::TestProcessdevem ser inseridos na parte superior do arquivo de fábrica, fora do FactoryGirl.define do...endbloco.
31414 Frank M.ehl
49

Sintaxe FG mais recente e sem inclusão necessária

factory :user do
  avatar { File.new(Rails.root.join('app', 'assets', 'images', 'rails.png')) }
end

ou, melhor ainda,

factory :user do
  avatar { File.new("#{Rails.root}/spec/support/fixtures/image.jpg") } 
end
Neal
fonte
2
Eu gosto dele, mas tenha em mente que o Rails 4 remove app / assets / images / rails.png, assim você terá que conta para essa mudança :)
stephenmurdoch
38

Desmond Bowe, do Pivotal Labs, sugere evitar fixture_file_upload devido a problemas de vazamento de memória. Em vez disso, você deve definir os campos de clipe de papel diretamente em sua fábrica:

factory :attachment do
  supporting_documentation_file_name { 'test.pdf' }
  supporting_documentation_content_type { 'application/pdf' }
  supporting_documentation_file_size { 1024 }
  # ...
end
Rick Quantz
fonte
Funciona para mim. Também é simples o suficiente para não exigir um arquivo existente na pasta spec / test.
Bibstha
2
Além de evitar vazamentos de memória, os testes serão mais rápidos.
Evedovelli 26/11/16
O post mencionado mudou-se para pivotal.io/blog
Karsten S.
Infelizmente, isso não funciona com a validação de anexo ativada content_type. Para isso, você precisa de um objeto de arquivo válido.
22417 Karsten S.
25

Eu tenho usado o código na essência abaixo:

Trilhos 2

http://gist.github.com/162881

Trilhos 3

https://gist.github.com/313121

deb
fonte
1
Apenas imaginando as mesmas coisas e isso funciona lindamente. Obrigado. Eu coloquei em um inicializador.
Todd
1
Algo a notar: A essência acima usa a sintaxe mais antiga do FactoryGirl. A resposta do DanS usa a nova sintaxe.
quer
12
file { File.new(Rails.root.join('spec', 'fixtures', 'filename.png')) }
Matthew Deiters
fonte
Esta é a solução funcional mais simples.
superluminary 12/10
4

Tente usar ActionController :: TestUploadedFile. Você pode apenas definir a propriedade do arquivo como uma instância de TestUploadedFile e o clipe de papel deve cuidar do resto. Por exemplo

valid_file = File.new(File.join(Rails.root, 'features', 'support', 'file.png'))  
p.images { 
   [
     ActionController::TestUploadedFile.new(valid_file, Mime::Type.new('application/png'))
   ] 
}
britt
fonte
0

As respostas acima, em alguns casos, podem ajudar, e a que realmente ajudou em uma de minhas situações, mas ao usar uma Carrierwave, a solução anterior desta pergunta não funcionou dessa vez.

PRIMEIRA ABORDAGEM:

Para mim, adicionar um after :createresolveu o problema para mim assim:

after :create do |b|
  b.update_column(:video_file, File.join(Rails.root, 'spec', 'fixtures', 'sample.mp4'))
end

Definir arquivo de vídeo embutido como video_file { File.new("#{Rails.root}/spec/fixtures/sample.mp4") }não deu certo e estava relatando erros.

SEGUNDA ABORDAGEM:

Defina uma fábrica como esta (mude personal_filepara o nome do seu anexo):

FactoryGirl.define do
  factory :contact do
    personal_file { File.new("#{Rails.root}/spec/fixtures/personal_files/my_test_file.csv") }
    personal_file_content_type 'text/csv'

  end
end

E adicione estas linhas ao config/environemnts/test.rb:

config.paperclip_defaults = {
  url: "#{Rails.root}/spec/fixtures/:attachment/:filename",
  use_timestamp: false
}
Aleks
fonte
-1

O que você está testando exatamente? Esse clipe de papel anexará com êxito o arquivo? Isso realmente parece um teste que o clipe de papel deve manipular, não o seu aplicativo.

Você tentou

a.data { File.join(Rails.root, 'features', 'support', 'file.png') }

Usamos Machinist em vez de factory_girl e acabamos de usar coisas como

Image.blueprint do
  image { RAILS_ROOT + 'spec/fixtures/images/001.jpg' }
end

No entanto, não estamos realmente testando muito quando fazemos isso, normalmente queremos apenas ter um Imageobjeto válido .

theIV
fonte
Eu tenho modelos que exigem uma associação válida com um modelo ativado por clipe de papel, ele próprio com um anexo válido.
Eric