How to test external request with Ministest / TestCase out of controller scope - ruby-on-rails

I'm new with Minitest particularly, at the moment everything goes ok because is not really hard to learn, but, I'm stuck with a routinary test: test a controller that upload a file to S3.
Goal:
Have a test that create a new Person.create() object with its file, in this case is a zip with some images.
Context:
I've a model Person with a file field with Paperclip and its configuration for S3.
I've various tests files for that (in tests/models & tests/controllers of course), but, with one more test in another folder because I'm testing other class that uses the Person object to update it.
My problem is that I've tried a lot of approach searching in Google and StackOverflow but I'm not sure how to address this tests out of the Controller scope.
require 'test_helper'
require 'webmock/minitest'
class PersonPhotosUpdateTest < ActiveSupport::TestCase
def setup
# some setup here
end
describe "My tests" do
test "Upload a zip file for Person" do
# My test here
end
end
end
I want in my test:
Create a new Person with post :create.
Person should be created with the file uploaded to S3 associated.
Assert that the file was uploaded to S3.
I suppose I need mock and/or stub for that but I'm not sure how with Minitest.
Thank you.

You may want to create an integration test:
require 'test_helper'
require 'webmock/minitest'
class LocationImporterTest < ActionDispatch::IntegrationTest
before do
stub_request(:any, "https://s3.amazonaws.com")
end
test "create" do
post "/foo", {
# params...
}
assert_requested :post, "https://s3.amazonaws.com",
:headers => {'Content-Length' => 3},
:body => "abc",
:times => 1 # ===> Success
end
end
See the webmock documents for how set expectations on the HTTP request, note that Test/Unit and Minitest are interchangable.

Related

Ruby on Rails Fixtures in sub-directories

I'm using Rails unit testing and fixtures framework. Unless there's a configuration I haven't seen, I can't use the "Advanced Fixtures" (no id objects) from fixtures in sub-directories:
fixtures/people.yml
_fixture:
model_class: Person
myself:
first_name: Me
last_name: Myself
The following call will pass as expected:
fixtures :people
assert(people(:myself)))
while this one will not (after I move people.yml to subdir):
fixtures "subdir/people"
assert(people(:myself)))
In the later case, the error I get is this:
NoMethodError: undefined method `people'
Using Advanced Fixtures seem valuable but having all of my fixture files in the root of /fixtures seems missing something. I have a few test files and I'd like to have various tests use different fixtures directories.
Any input will be appreciated.
I would try saving people.yml in /fixtures/subdir/people.yml. The documentation is here https://apidock.com/rails/ActiveRecord/TestFixtures/ClassMethods/set_fixture_class .
test_helper.rb
set_fixture_class :people => 'Subdir::People'
in your test file.
before do
myself = subdir_people(:myself)
register(myself)
end
it "should test myself" do
assert(people(:myself)))
end
Here is my final solution:
In the fixture file, I kept:
_fixture:
model_class: Person
In the test file, I got:
def people(sym)
subdir_people(sym)
end
def test_myself
assert(people(:myself))
end
That way, it saves me from refactoring my numerous people() calls. Or I can use subdir_people() if I want.

How to write rspec for rails jobs

Hi i am working with a RoR project with ruby-2.5.0 and rails 5. I am using AWS SQS. I have created a job as follows:-
class ReceiptsProcessingJob < ActiveJob::Base
queue_as 'abc'
def perform(receipt_id)
StoreParserInteractor.process_reciept(receipt_id)
end
end
Now i want to write unit test for it. I tried like:-
# frozen_string_literal: true
require 'rails_helper'
describe ReceiptsProcessingJob do
describe "#perform_later" do
it "scan a receipt" do
ActiveJob::Base.queue_adapter = :test
expect {
ReceiptsProcessingJob.perform_later(1)
}.to have_enqueued_job
end
end
end
But it doesnot cover StoreParserInteractor.process_reciept(receipt_id). Please help how can i cover this. Thanks in advance.
The example is testing the job class. You need to write a spec for StoreParserInteractor and test the method process_reciept.
Something along the lines of (pseudo code):
describe StoreParserInteractor do
describe "#process_receipt" do
it "does that" do
result = StoreParserInteractor.process_receipt(your_data_here)
expect(result to be something)...
end
end
end
But, the Rails guide suggests this kind of test:
assert_enqueued_with(job: ReceiptsProcessingJob) do
StoreParserInteractor.process_reciept(receipt_id)
end
Maybe this increases code coverage as well.
In my opinion, you shouldn't actually test the ActiveJob itself, but the logic behind it.
You should write a test for StoreParserInteractor#process_reciept. Think of ActiveJob as an "external framework" and it is not your responsibility to test the internals of it (e.g. if the job was enqueued or not).
As kitschmaster said, don't test ActiveJob classes, in short

RSpec returns all objects to be 'nil'

I am new to Rspec. I am writing a test case to cover some action in a model. Here is my rspec code
test_cover_image_spec.rb
require 'spec_helper'
describe Issue do
before :each do
#issue = Issue.joins(:multimedia).uniq.first
binding.pry
end
describe '#release_cover_image' do
context 'While making an issue open' do
it 'should make issue cover in S3 accessible' do
put :update, :id => #issue.id, :issue => #issue.attributes = {:open => '1'}
end
end
end
end
#issue always returns nil. In my debugger also, Issue.all returns an empty array.
Tests usually run in isolation. That means each test needs to set up the objects before running. After the test run common test configurations delete all created data from the test database. That means you need to create your test data before you can use it.
For example like this:
require 'spec_helper'
describe Issue do
# pass all attributes to create a valid issue
let(:issue) { Issue.create(title: 'Foo Bar') }
describe '#release_cover_image' do
context 'While making an issue open' do
it 'should make issue cover in S3 accessible' do
put :update, id: issue.id, issue: { open: '1' }
expect(issue.reload.open).to eq('1')
end
end
end
end
To make this work you have to populate the test database first.
Check out factory_girl gem - it is most often used for easy generating test data.
So (general idea is that) you will have to create few factories:
issues_factory.rb
multimedia_factory.rb
And use them, to generate the issue object prior the test run.
If you're not going to use factory_girl then anyway you should change from creating an issue in before block to using let:
let(:issue) { Issue.create }

Session Reset For Each Performance Test

Using the example from http://guides.rubyonrails.org/performance_testing.html#examples (1.2.1):
require 'test_helper'
require 'rails/performance_test_help'
class PostPerformanceTest < ActionDispatch::PerformanceTest
def setup
# Application requires logged-in user
login_as(:lifo)
end
def test_homepage
get '/dashboard'
end
def test_creating_new_post
post '/posts', :post => { :body => 'lifo is fooling you' }
end
end
Per the documentation, it says setup is called once per test, but when I run a test like the example, the session is maintained, and the next setup call uses the old session. I used reset!, to clear the session in the setup.
Is that correct? Do I also need to do the same thing in integration tests? Why in the world is the session maintained? Maybe I am missing something, like if there is a technique to see if logged/session exists?

How do I test a file upload in rails?

I have a controller which is responsible for accepting JSON files and then processing the JSON files to do some user maintenance for our application. In user testing the file upload and processing works, but of course I would like to automate the process of testing the user maintenance in our testing. How can I upload a file to a controller in the functional testing framework?
Searched for this question and could not find it, or its answer on Stack Overflow, but found it elsewhere, so I'm asking to make it available on SO.
The rails framework has a function fixture_file_upload (Rails 2 Rails 3, Rails 5), which will search your fixtures directory for the file specified and will make it available as a test file for the controller in functional testing. To use it:
1) Put your file to be uploaded in the test in your fixtures/files subdirectory for testing.
2) In your unit test you can get your testing file by calling fixture_file_upload('path','mime-type').
e.g.:
bulk_json = fixture_file_upload('files/bulk_bookmark.json','application/json')
3) call the post method to hit the controller action you want, passing the object returned by fixture_file_upload as the parameter for the upload.
e.g.:
post :bookmark, :bulkfile => bulk_json
Or in Rails 5: post :bookmark, params: {bulkfile: bulk_json}
This will run through the simulated post process using a Tempfile copy of the file in your fixtures directory and then return to your unit test so you can start examining the results of the post.
Mori's answer is correct, except that in Rails 3 instead of "ActionController::TestUploadedFile.new" you have to use "Rack::Test::UploadedFile.new".
The file object that is created can then be used as a parameter value in Rspec or TestUnit tests.
test "image upload" do
test_image = path-to-fixtures-image + "/Test.jpg"
file = Rack::Test::UploadedFile.new(test_image, "image/jpeg")
post "/create", :user => { :avatar => file }
# assert desired results
post "/create", :user => { :avatar => file }
assert_response 201
assert_response :success
end
I think it's better to use the new ActionDispatch::Http::UploadedFile this way:
uploaded_file = ActionDispatch::Http::UploadedFile.new({
:tempfile => File.new(Rails.root.join("test/fixtures/files/test.jpg"))
})
assert model.valid?
This way you can use the same methods you are using in your validations (as for example tempfile).
From The Rspec Book, B13.0:
Rails’ provides an ActionController::TestUploadedFile class which can be used to represent an uploaded file in the params hash of a controller spec, like this:
describe UsersController, "POST create" do
after do
# if files are stored on the file system
# be sure to clean them up
end
it "should be able to upload a user's avatar image" do
image = fixture_path + "/test_avatar.png"
file = ActionController::TestUploadedFile.new image, "image/png"
post :create, :user => { :avatar => file }
User.last.avatar.original_filename.should == "test_avatar.png"
end
end
This spec would require that you have a test_avatar.png image in the spec/fixtures directory. It would take that file, upload it to the controller,
and the controller would create and save a real User model.
You want to use fixtures_file_upload. You will put your test file in a subdirectory of the fixtures directory and then pass in the path to fixtures_file_upload. Here is an example of code, using fixture file upload
If you are using default rails test with factory girl. Fine below code.
factory :image_100_100 do
image File.new(File.join(::Rails.root.to_s, "/test/images", "100_100.jpg"))
end
Note: you will have to keep an dummy image in /test/images/100_100.jpg.
It works perfectly.
Cheers!
if you are getting the file in your controller with the following
json_file = params[:json_file]
FileUtils.mv(json_file.tempfile, File.expand_path('.')+'/tmp/newfile.json')
then try the following in your specs:
json_file = mock('JsonFile')
json_file.should_receive(:tempfile).and_return("files/bulk_bookmark.json")
post 'import', :json_file => json_file
response.should be_success
This will make the fake method to 'tempfile' method, which will return the path to the loaded file.

Resources