Use RSpec's "expect" etc. outside a describe ... it block - ruby

I'm building a web-app automation framework that is designed to allow for:
automating tasks, of course
building test scenarios easily
I'm using Capybara to communicate with a browser and I have a library of components with a number of helper functions (login_to_the_back_office, create_customer, etc.).
Now I would like my components to be usable standalone as well as within RSpec tests. This means that my components, as included in the library, are not wrapped around describe... it... blocks by default, but they will be at some point when a test uses them and so they should use expect and friends as often as possible.
I followed rspec's .should fails (outside describe/it block) in Ruby 2? to enable should and it works (actually just requiring rspec-expectations was enough in my case), but I can't figure out how to make expect work.
I think expect is defined here https://github.com/rspec/rspec-expectations/blob/master/lib/rspec/expectations/syntax.rb but my knowledge of Ruby's meta-magic is too limited to figure out how to make expect available to my class.
I've tried including RSpec::Expectations::Syntax in my class but I still get undefined method 'expect' whenever I try to use it.
How can I use expect outside of describe... it... ?

include ::RSpec::Matchers
class A
include ::RSpec::Matchers
def test
expect('1'.to_i).to eq 1
end
def failed_test
expect('1'.to_i).to eq 2
end
end
A.new.test
# => true
A.new.failed_test
# RSpec::Expectations::ExpectationNotMetError:
# expected: 2
# got: 1

Related

In a Ruby module, how do you test if a method exists in the context which use the module?

Some context
I'm playing with Ruby to deepen my knowledge and have fun while at the same time improving my knowledge of Esperanto with a just starting toy project called Ĝue. Basically, the aim is to use Ruby facilities to implement a DSL that matches Esperanto traits that I think interesting in the context of a programming language.
The actual problem
So a first trait I would like to implement is inflection of verbs, using infinitive in method declaration (ending with -i), and jussive (ending with -u) for call to the method.
A first working basic implementation is like that:
module Ĝue
def method_missing(igo, *args, &block)
case igo
when /u$/
celo = igo.to_s.sub(/u$/, 'i').to_s
send(celo)
else
super
end
end
end
And it works. Now the next step is to make it more resilient, because there is no guaranty that celo will exists when the module try to call it. That is, the module should implement the respond_to? method. Thus the question, how do the module know if the context where module was required include the corresponding infinitive method? Even after adding extend self at the beginning of the module, inside of the module methods.include? :testi still return false when tested with the following code, although the testu call works perfectly:
#!/usr/bin/env ruby
require './teke/ĝue.rb'
include Ĝue
def testi; puts 'testo!' ;end
testu
Note that the test is run directly into the main scope. I don't know if this makes any difference with using a dedicated class scope, I would guess that no, as to the best of my knowledge everything is an object in Ruby.
Found a working solution through In Ruby, how do I check if method "foo=()" is defined?
So in this case, this would be checkable through
eval("defined? #{celo}") == 'method'

Chef NoMethodError when using library module method with registry_key in recipe

I receive the error
NoMethodError
-------------
undefined method `registry_key' for HashOperations:Module
when converging a Chef cookbook.
This is the short version of the code from libraries/hash_operations.rb:
module HashOperations
# Tried: require 'chef/mixin/shell_out'
# Tried: include Chef::Mixin::ShellOut
def self.create_reg_keys(item_hash)
item_hash.each do |item_name, item_props|
# (...) construct the item_keys array
registry_key "HKEY_LOCAL_MACHINE\\SOFTWARE\\#{item_name}" do
recursive true
values item_keys
action :create
end
end
end
def self.generate_reg_keys_for_item_key(...)
# some hash operations here
create_reg_keys(item_hash)
end
end unless defined?(HashOperations)
class Chef::Resource
# Tried: Chef::Resource.send(:include, HashOperations)
# Tried: include HashOperations
extend HashOperations
end
and here is recipes/default.rb:
Chef::Resource.send(:include, HashOperations)
ruby_block "test" do
block do
# Tried: Chef::Recipe.send(:include, HashOperations)
items_keys.each do |item_key|
HashOperations.generate_reg_keys_for_item_key(..., item_key)
end
end
end
I guess the main problem comes from trying to use a Chef resource, registry_key, inside a method inside a module, which in turn is called from the recipe.
I have a working version if I'm not using a module, but I need a module if I want to test the code with ChefSpec, as several articles pointed (like this one: Stubbing library class methods in ChefSpec )
The link mentioned above is the reason for which I use end unless defined?(HashOperations) inside the module.
I've tried using include statements, it can be seen in the comments, or in the recipe's first line, as several StackOverflow posts suggested, with no luck. One post was discussing the usage of LWRP, but I really don't think it's the case here, as the code is strictly related to this recipe and wouldn't be used in some other cookbook.
As a note: I'm using self. in order for the defs to be visible to one another, otherwise I receive an error about generate_reg_keys_for_item_key not being available.
So, taking into account the fact that I've spent quite some hours searching for solutions on this, including the ones suggested by StackOverflow, the questions: what's the best approach to solve this error and have a simple solution that can be tested with ChefSpec (I'm not entirely excluding LWRPs though), and WHAT and HOW should I include for the registry_key to be visible on converge operation ?
You can't use the recipe DSL from helpers like this unless the helper is itself set up as a DSL extension. Check out https://coderanger.net/chef-tips/#3 to see how to do that.

Disambiguate Function calls in Ruby

I am working through Learn Ruby The Hard Way and came across something intriguing in exercise 49.
In parser.rb I have a function named skip(word_list, word_type) at the top level, which is used to skip through unrequited words (such as stop words) in user input. It is not encapsulated in a class or module. As per the exercise I have to write a unit test for the parser.
This is my code for the Unit Tests:
require "./lib/ex48/parser"
require "minitest/autorun"
class TestGame < Minitest::Test
def test_skip()
word_list = [['stop', 'from'], ['stop', 'the'], ['noun', 'west']]
assert_equal(skip(word_list, 'stop'), nil)
assert_equal(skip([['noun', 'bear'], ['verb', 'eat'], ['noun', 'honey']], 'noun'), nil)
end
end
However, when I run rake test TESTOPTS="-v" from the command line, these particular tests are skipped. This seems to be because there is a clash with the skip method in the Minitest module because they run perfectly after I change the name to skip_words.
Can someone please explain what is going on here exactly?
"Top level functions" are actually methods too, in particular they are private instance methods on Object (there's some funkiness around the main object but that's not important here)
However minitest's Test class also has a skip method and since the individual tests are instance methods on a subclass of Test you end up calling that skip instead.
There's not a very simple way of dealing with this - unlike some languages there is no easy way of saying that you want to call a particular superclass' implementation of something
Other than renaming your method, you'll have to pick an alternative way of calling it eg:
Object.new.send(:skip, list, type)
Object.instance_method(:skip).bind(self).call(list, type)
Of course you can wrap this in a helper method for your test or even redefine skip for this particular Test subclass (although that might lead to some head scratching the day someone tries to call minitest's skip.

Calling a Method - Watir-Webdriver

I am new to automation and I have started using Watir Webdriver to automate a website. However, there are certain pieces of code which can be reused for multiple test cases. How can I group the reusable pieces of code into a method which I can call in every test case ? Could you please provide references or examples ?
You are interested in methods.
Here is an example:
def automation_snippet
#browser.text_field(:id => 'field_id').set 'foo'
end
Later you will probably be interested in classes, page objects, and modules.
This is basic ruby stuff. You will need to make sure that whatever method you want to use is in scope, too.
So, keywords to start with are methods and scope. If you are using Cucumber, then you can define methods in any step definition file and they will be available in all your other Cucumber tests.

Mocking constructors in Ruby

I'm a Java-developer toying with Ruby, and loving it. I have understood that because of Ruby's metaprogramming facilities my unit-tests become much cleaner and I don't need nasty mocking frameworks. I have a class which needs the File class's services and in my test I don't want to touch my real filesystem. In Java I would use some virtual file system for easier "seams" to pass fake-objects in but in Ruby that's obviously overkill. What I come up seems already really nice compared to the Java-world. In my class under test I have an optional constructor parameter:
def initialize(file_class=File)
When I need to open files within my class, I can then do this:
#file_class.open(filename)
And the call goes to either the real File-class, or in case of my unit-test, it goes to a fake-class which doesn't touch the filesystem. I know there must be a better way to do this with metaprogramming?
Mocha (http://mocha.rubyforge.org/) is a very good mocking library for ruby. Depending on what you're actually wanting to test (i.e. if you want to just fake out the File.new call to avoid the file system dependency or if you want to verify that the correct arguments are passed into File.new) you could do something like this:
require 'mocha'
mock_file_obj = mock("My Mock File") do
stubs(:some_instance_method).returns("foo")
end
File.stubs(:new).with(is_a(String)).returns(mock_file_obj)
In the case you've outlined, I'd suggest that what you're doing seems fine. I know that it's a technique that James Mead (the author of Mocha) has advocated. There's no need to do metaprogramming just for the sake of it. Here's what James has to say about it (and a long list of other techniques you could try)
This is a particularly difficult challenge for me. With the help I received on this question, and some extra work on my behalf, here's the solution I arrived at.
# lib/real_thing.rb
class RealThing
def initialize a, b, c
# ...
end
end
# test/test_real_thing.rb
class TestRealThing < MiniTest::Unit::TestCase
class Fake < RealThing; end
def test_real_thing_initializer
fake = mock()
Fake.expects(:new).with(1, 2, 3).returns(fake)
assert_equal fake, Fake.new(1, 2, 3)
end
end

Resources