Ruby on Rails - Randomly failing RSpec tests - ruby-on-rails

Sorry for my stupid question.
I added the external api to get response in order to check zipcode's validation.
app/validators/zipcode_validator.rb
class ZipcodeValidator < ActiveModel::Validator
def validate(record)
record.errors.add(:zipcode, :blank) if record.zipcode.blank?
record.errors.add(:zipcode, :not_found) if WmsService.wms_delivery_dates(record.zipcode).nil?
end
end
It works fine in real but failed randomly and took more time when I run rspec.
What's the good solution for this situation?

You should not call external APIs in your tests. There are several methods to avoid it:
VCR gem records API response in first call then then replies it from disk (that is fast and reliable).
Mocking HTTP calls, e.g. with WebMock. You need to write specify mocked request and write a response in your specs. It may help you improve test readability also it helps in testing edge cases.
Wrap your service call in your class and replace it with an RSpec stub.
Wrap your service call in your class that accepts adapters. An adapter is responsible for calling external service. In test env pass a test adapter with predetermined responses.

Related

how do I test a private method on controller, using minitest?

Very new to testing
I have a model-less rails app with a controller called "PokerController"
in poker_controller.rb, I have a private method that looks like this:
private
def test(x)
1 + x
end
Then I have a file called 'poker_controller_test.rb' in test/controllers
I'm trying to do something like this:
require 'test_helper'
class PokerControllerTest < ActionController::TestCase
test "check if test == 2" do
test = test(1)
assert test == 2
end
end
As you can see, I'm trying to save the result of the function 'test' being called, to a variable called 'test' then I'm checking to see if that == 2.
Basically, I'm trying to pass in a number (in this case '1') to the test method, and I want the test to add 1 to 1, and expect to get 2.
I'm sure I'm just not setting up the test right, but how do I call a custom method like this and then evaluate what's returned?
Here's my result from the error in the test:
Error:
PokerControllerTest#test_check_if_test_==_2:
ArgumentError: unknown command "\x01"
test/controllers/poker_controller_test.rb:6:in `test'
test/controllers/poker_controller_test.rb:6:in `block in <class:PokerControllerTest>'
You don't.
Testing private methods in general is frowned upon*.
In Rails you mainly test controllers through integration tests. These are tests that send real HTTP requests to your application and then you write assertions about the response or the side effects of sending the request.
"[...] You can test what cookies are set, what HTTP code is returned, how the view looks, or what mutations happened to the DB, but testing the innards of the controller is just not a good idea."
- David Heinemeier Hansson
An example of an integration test is:
require "test_helper"
class PokerControllerTest < ActionDispatch::IntegrationTest
test "can see the welcome page" do
get "/path/to/somewhere"
assert_select "h1", "Welcome to my awesome poker app"
end
end
If you have a method that you want to test in isolation it does not belong in your controller in the first place and arguably it should not be a private method either.
You should test the public API of your objects. The public API of a controller is the methods that respond to HTTP requests via the router.
The use of ActionController::TestCase is discouraged outside of legacy applications.
New Rails applications no longer generate functional style controller
tests and they should only be used for backward compatibility.
Integration style controller tests perform actual requests, whereas
functional style controller tests merely simulate a request. Besides,
integration tests are as fast as functional tests and provide lot of
helpers such as as, parsed_body for effective testing of controller
actions including even API endpoints.
Besides that the issues with your test is that you're calling the test method on the instance of ActionController::TestCase and not your controller. You're then shadowing the method by defining an instance variable with the same name which is rarely a good thing.
Even if you did call the method on the controller Ruby would still raise an error since you're calling a private method from outside the object - thats the whole point of private methods.
While you could violate encapsulation by calling #controller.send(:test) this is just a bad idea on so many levels.

Enforce Webmock stub_request to raise at the end of a test if it has not been called

We have a RoR application, Rspec for tests with Webmock for HTTP requests.
After having to do some refactoring in our legacy codebase, I realized that many of our tests had unnecessary stubs.
Like this example, the do_a function has been refactored so that we don't do any api call so the stub_request is not necessary anymore, worse, it should be removed.
it 'does something' do
stub_request(:get, 'http://something.com/users/123')
do_a
expect(..)
end
One way of fixing this is:
it 'does something' do
stub_something = stub_request(:get, 'http://something.com/users/123')
do_a
expect(..)
expect(stub_something).to have_been_requested.once
end
But I'd like to enforce this directly through a strict mode where the test fails if any declared stub has not been called ? The first example would then fail automatically.
Thanks a lot for your help
You want to use expectations instead of stub_request:
expect(WebMock).to have_requested(:get, "http://something.com/users/123").once
# or
expect(a_request(:get, "http://something.com/users/123")).to have_been_made.once
But I'd like to enforce this directly through a strict mode where the test fails if any declared stub has not been called?
I don't think this is really possible unless you do some heavy monkeypatching - and it seems like a bad idea instead of just refactoring your tests.

Is there a better way to test that a script is being called in a controller other than expect_any_instance_of(#{ControllerClass}).to receive?

I'm fairly new to using RSpec, so there's a lot I still don't know. I'm currently working on speccing out a section of functionality which is supposed to run a script when a button is pressed. The script is currently called in a controller, which I don't know if there's a good way to test.
I'm currently using
expect_any_instance_of(ConfigurationsController)
.to receive(:system)
.with('sh bin/resque/kill_resque_workers')
.and_return(true)
in a feature spec and it works, but rubocop is complaining about using expect_any_instance_of and I've been told to only use that method if there was no better way.
Is there any better way to test this? Like is there a way to get the instance of the controller being used, or a better kind of test for this?
A better pattern would be to not inline the system call in your controller in the first place. Instead create a seperate object that knows how to kill your worker processes and call that from your controller. The service object pattern is often used for this. It makes it much easier to stub/spy/mock the dependency and make sure it stops at your application boundry.
It also lets you test the object in isolation. Testing plain old ruby objects is really easy. Testing controllers is not.
module WorkerHandler
def self.kill_all
system 'sh bin/resque/kill_resque_workers'
end
end
# in your test
expect(WorkerHandler).to receive(:kill_all)
If your service object method runs on instances of a class you can use stub_const to stub out the new method so that it returns mocks/spies.
Another more novel solution is dependency injection via Rack middleware. You just write a piece of middleware that injects your object into env. env is the state variable thats passed all the way down the middleware stack to your application. This is how Warden for example works. You can pass env along in your spec when you make the http calls to your controller or use before { session.env('foo.bar', baz) }.

Outputting HTTP request/response data with RSpec

I have a rails project that serves a JSON API with tests written in RSpec. Often when running specs (request specs, specifically), I’m interested in seeing some details about the HTTP request/response...i.e. the request URL, request body, and response body, ideally JSON pretty-formatted for readability. This isn't for the purposes of documentation but rather as part of the development / debugging process.
I have a helper method I wrote which does this...you just drop a method call into your spec and it prints this stuff out.
But, seems like it would be better if there was a switch that’s part of the running specs. RSpec has custom formatters which I thought might be the right direction, but in trying to build one, I can't figure out how to get access to the request/response objects like you can from inside of your spec.
How can I access the request/response objects in my custom RSpec formatter? Or, perhaps another way to approach the problem?
Here's an approach:
Assuming a rails project, in spec_helper.rb, define a global "after" hook like so:
config.after(:each) do #runs after each example
if ENV['PRINTHTTP']
#use request/response objects here, e.g. puts response.status
end
end
Then, you can conditionally enable by adding the environmental variable on the command-line:
$ PRINTHTTP=1 rspec

Using rspec to test code prone to external infuence

I'm using rspec to test a code that may fail depending on the change of a site structure (the external influence I mentioned). I would like to write an example that involves "should raise an error" but I'm not sure if rspec is the right tool to test code in such situations. Could someone point me in some direction?
Thanks in advance
You could write custom matchers
Something like :
site.should_have_valid_structure
Spec::Matchers.define :have_structure
match do |actual|
actual.structure == Site::VALID_STRUCTURE
end
end
Mock the external influence so you can test it properly (if the external influence is a Web page or other HTTP request, WebMock and VCR are great for this). Your tests should not rely on anything external functioning properly -- or improperly. See http://marnen.github.com/webmock-presentation/webmock.html for an overview I wrote last year.

Resources