I maintain a gem that gathers data from various APIs. For APIs that require an API key, I have implemented scrapers to fall back on when no API key is set.
My current solution to testing both the scraper and the API code is to have two spec files that are virtually the same except for a before hook.
In the API spec, I have a before(:each) hook that explicitly sets the API key.
In the scraper spec, I have a before(:each) hook that explicitly nullifies the API key.
Aside from those two different hooks, the specs are essentially the same. However, there are one or two instances where the API spec differs from the scraper spec, so I'd need the ability to handle that. I imagine just checking to see whether or not the API key is set will be enough for this.
Another thing to note is that I don't want to rerun my entire test suite, as only a very small fraction of the codebase has both an API and scraper. I basically need to be able to set it on a per-spec basis.
Thanks in advance for any help that you guys can provide!
EDIT: Think I have a solution. Will provide details when I get a full implementation.
This is what RSpec's shared examples are for. Use like so:
RSpec.shared_examples_for "an API component" do
let(:api_client) { APIClient.new }
it "foos" do
expect(api_client).to foo
end
it "bars" do
expect(api_client).to bar
end
end
RSpec.describe API do
it_behaves_like "an API component" do
before(:each) { set_api_key }
end
end
RSpec.describe Scraper do
it_behaves_like "an API component" do
before(:each) { nullify_api_key }
end
end
Related
I am using the PageObject pattern, which abstracts HTML details away from the top-level of the test. I am doing this using the SitePrism gem.
In my example, I have a home page (HomePage.rb):
class HomePage < SitePrism::Page
set_url ENV['BASE_URL']
section :header, HeaderSection, "div[class='headerSection']"
which refers to a common page section, HeaderSection (HeaderSection.rb):
class HeaderSection < SitePrism::Section
element :sign_in_button, "a[class='signIn']"
and a step definition for my tests (login.rb):
And(/^I am not logged in/) do
#home_page = HomePage.new # actually, this is specified in env.rb
expect(#home_page.header).to have_sign_in_button
end
Instead of exposing the web element to the step definition, I want to encapsulate this within a class method of HomePage. It seems that the best way to do that is to put the assertion into a class method within HomePage itself (HomePage.rb):
def amILoggedIn
expect(header).to have_sign_in_button
end
The above assumes that I am also using include RSpec::Matchers.
My method would then be the only thing exposed to the step definition (login.rb):
And(/^I am not logged in/) do
#home_page.amILoggedIn
end
As part of SitePrism, the sign_in_button element has its own built-in methods to check for its presence, which is:
header.has_sign_in_button?
Question
In terms of best practice, which is the most recommended way of writing this assertion within the amILoggedIn method (even though it appears they both use the same instruction),
expect(header).to have_sign_in_button
or
header.has_sign_in_button?
expect(header).to have_sign_in_button and header.has_sign_in_button? do two different things. The first is an assertion (raises an exception if it fails) and the second just returns a boolean response. If what you want is an assertion you could assert on the boolean response assert header.has_sign_in_button? or expect(header.has_sign_in_button?).to be true but the failure message from have_sign_in_button is going to be a lot more descriptive.
Old question but providing updated answer
Given SitePrism now uses implicit waiting by default as Thomas has said you've got two different method signatures, and two different outputs.
Also dependent on who you speak to, it is considered poor practice to include the matchers in a variety of places. Using Cucumber with SitePrism gives you a nice separation of concerns to perform the testing in the Cucumber World (step_definitions), and isolate all other modelling concerns to the support code.
Using SitePrism as described by the official docs will allow you (when using cucumber), to access all rspec methods in the cucumber world, and test easily and effectively.
Is there a way to hook in some code after any raise is done in ruby? The idea is to provide some extra information about the error in a centralized location, such as follows:
def after_raise
logger.error "some information here"
end
def logger
#logger ||= Logger.new(my_log_output)
end
Edit: Maybe it will help if I'm more specific about what this is for.
I'm working a large project with multiple domains. There are times that raised errors are just not descriptive enough - today I was talking with one of my team-members and we discussed ways to work around this. It's not that the original raise messages are awful/completely nondescript (although, I'm sure that's the case in one or two places), just that as the project has evolved, new information has become useful for troubleshooting. We'd like to create something that can extend existing code.
Each domain is able to be tested alone, but plugs into an overall infrastructure. If we added this to the 'main' domain, when the application as a whole is running (for example, during end-to-end tests) this would be applied to all instances where raise is called. If we were testing a single domain, it would not apply.
You can redefine the Kernel#raise method, keeping the original method through an alias:
module Kernel
alias_method :_raise, :raise # original method is now _raise
def raise(*args)
# do hook stuff
_raise(*args)
end
end
I have a series of RSpec tests - each one living in their own files - that all need a specific object to be ran (shop).
Since setting up the shop object is complicated, I want to have a master file that builds the shop object and then passes it to all the RSpec test files in turn. The idea is that those writing the test scripts do not need to know anything about the setup step.
I could not figure out how to bring something from the outside world into an rspec test case. I have tried variations around the lines of this:
file master.rb:
describe "Testing tests/some_test.rb" do
before :all do
#shop = some_complex_operations
end
load 'tests/some_test.rb'
end
file tests/some_test.rb:
describe 'Some feature' do
it 'should know about #shop'
expect(#shop).not_to be nil
end
end
When I run rspec master.rb then some_test.rb fails.
I know what I outlined is probably a very wrong way to go about this, but I hope at least you get the idea.
How can I implement such a thing cleanly?
What you need is a global hook.
You can create a spec_helper and add there a before all global hook. For example:
RSpec.configure do |config|
config.before(:each) do
#shop = "in case you need a fresh instance of shop each time"
end
config.before(:all) do
#shop = "in case you need a single instance (i.e. you don't modify it)"
end
end
Then you should require this helper from each of your specs. The spec_helper can store any global hooks as well as helpers, or other dependencies for your tests.
When you are writing a test, it should test one thing. shop might be a very complex object, which many objects and methods interact with, but I should guess none of them are aware of the complexity of shop - each method is interested in some aspect of shop.
Therefore I suggest that instead of building a complex object for each test, make a double of it, make it expect what's relevant, and then simply stub the rest:
describe 'Some feature' do
let(:shop) { double('shop').as_nil_object }
let(:my_object) { MyClass.new(shop: shop) }
it 'should do something awesome with shop' do
expect(shop).to receive(:awesome_data).and_return(my_data: 'is_this')
expect(my_object.do_something_awesome).to eq 'how awesome is_this?'
end
end
I'm attempting to create a restful, json api in ruby - so I'm using grape (https://github.com/intridea/grape) inside of Rack.
I'm not using Rails for this project, so cancan, sorcery, etc... don't seem to be the best options. Plus, I'd hate to mix in a bunch of imperative logic into grape's declarative DSL.
While grape has built in authentication support, I do not see anything about authorization. It seems like this would be a common enough use case that this road would have been traveled before, but after some pretty thorough digging in google and the grape codebase itself I have turned up nothing.
Has anyone implemented something like this for their project in grape? What did you use?
This may be a little too late, but anyway. I'd recommend you use Pundit for authorization, it's deadly simple. To use it in your Grape API endpoints, you would need to include Pundit helpers:
class API < Grape::API
format :json
helpers Pundit
helpers do
def current_user
resource_owner
end
end
mount FoosAPI
end
Now in you API endpoints, you should be able to use authorize foo, action? as you would always do in Rails controllers:
class FoosAPI < Grape::API
get ':id' do
foo = Foo.find(params[:id])
authorize foo, :show?
present foo, with: FooEntity
end
end
Hope it helps!
I thought I can give short comment on this, but the field is to short, sorry if it will not be right answer but:
You mentioned sorcery - I think it is authentication system and got nothing to do with an authorization. (I do not know sorcery gem implementation - just repeating statement from documentation and assuming that description enumerates such systems it replaces and it is valid definition). I guess it is just mistake.
Fundamental question you should ask yourself is...
How much role-based system do you develop? I think if this is only matter of public/private/admin roles probably you should consider just moving it to different APIs.
That can be cumbersome in some circumstances but worth a try for not complicated none additive roles. Simple mount in grape will solve the problem OOTB.
Real problem is if you think about some expandable/dynamic role system or you want to be just DRY. That can be painful ;-). I think Rayan's Bytes cancan gem implementation should help you understand how such problem can be solved on higher abstract level. For particular (without higher abstraction - such as dynamic roles) implementation it should be fine to just use currently given helpers from grape and delegate their responsibilities to model (basic usage).
helpers do
def current_user
#current_user ||= User.authorize!(env)
end
def authenticate!
error!('401 Unauthorized', 401) unless current_user
end
end
so all the story is about how to implement User.authorize!(env) and I believe that should be done in your model and depends strictly on your needs.
I don't know whether my answer is in time for you. I recently have the same problem with the Grape and authorization in a Rails4 project. And after trying, I found out a way for this.
In my project, I'm using the pundit for authorization, it asks me to create a policy folder, and create the authorize rules for each Model, each rule is a Ruby class, something like this(from pundit Github page)
class PostPolicy < ApplicationPolicy
def update?
user.admin? or not record.published?
end
end
then in the Grape API, I just use this class for the authorization, code like this:
desc "hide a post"
post :hide do
authenticate!
error!( "user unauthorized for this" ) unless PostPolicy.new(current_user, #post).hide_post?
#post.update hidden: true
{ hidden: #post.hidden }
end
the authenticate! and current_user helpers are customized helpers. By this way, I can reuse the authorize rules created when developing website parts.
This works for me. Hope the Pundit way can solve your problems for Grape authorization
I want to run a given suite of tests several times - once as each of the basic types of system users. (It tests the various features that should be shared by them). More generally, I often end up wanting to run the same set of tests under different conditions.
run_as_each(user_list) do |user|
describe "When visiting the front page with #{user.name}" do
it "should see the welcome message" do
....
end
it "should be able to login" do
....
end
end
end
But this isn't working - It says my method "run_as_each" is undefined - in fact, it seems helpers can only be used inside of the actual specific tests, "it"s. So what should I do?
So what is another way I can approach this problem - specifically, to run a batch of tests for several different users, or at the least to define within a test which users it should be run for?
Have you looked into using shared example? You mentioned types of users, so something like this might work.
shared_examples "a user" do
let(:user) { described_class.new }
describe "When visiting the front page" do
it "should see the welcome message" do
....
end
it "should be able to login" do
....
end
end
end
describe UserTypeA do
it_behaves_like 'a user'
end
describe UserTypeB do
it_behaves_like 'a user'
end
I didn't run the code, so syntax errors are probable.
More from the docs
I was able to get around this problem by including a file with a namespaced module that included all the "helper" methods I needed to use for dynamic code generation. The actual test helpers, if included through the rspec config, are only loaded when the tests are being executed - they are, thus, not available to the any code that occurs outside of tests.