How do I call a method from an rspec test - ruby

I'm learning Ruby and TDD (rspec) at the same time.
I've written the following test:
describe is_eligible do
it "returns true if the passed in string is not part of a list" do
result = is_eligible("abc")
result.should eq(false)
end
end
It is testing the following code:
def is_eligible(team_name)
array = Array.new
array << "abc" << "def" << "ghi"
if array.include?(team_name)
return false
else
return true
end
end
I'm receiving the following error and can't find out why.
*/Users/joel.dehlin/top32/lib/ineligible_teams.rb:6:in `is_eligible': wrong number of arguments (0 for 1) (ArgumentError)*
Any help is appreciated!

The problem is that the describe method expects a string or something that can evaluate to string. If you say "is_eligible" without the quotes, it will actually try to call the method and you get the error.
describe "is_eligible" do
it "returns true if the passed in string is not part of a list" do
result = is_eligible("abc")
result.should eq(false)
end
end

Related

Syntax error, unexpected tIDENTIFIER, expecting ')' Ruby

I get the following error when running a simple method that takes in a proper noun string and returns the string properly capitalized.
def format_name(str)
parts = str.split
arr = []
parts.map do |part|
if part[0].upcase
else part[1..-1].downcase
arr << part
end
end
return arr.join(" ")
end
Test cases:
puts format_name("chase WILSON") # => "Chase Wilson"
puts format_name("brian CrAwFoRd scoTT") # => "Brian Crawford Scott"
The only possibility that the above code returns a blank output is because your arr is nil or blank. And the reason your arr is blank(yes it is blank in your case) because of this line of code:
if part[0].upcase
in which the statement would always return true, because with every iteration it would check if the first element of the part string can be upcased or not, which is true.
Hence, your else block never gets executed, even if this got executed this would have returned the same string as the input because you are just putting the plain part into the array arr without any formatting done.
There are some ways you can get the above code working. I'll put two cases:
# one where your map method could work
def format_name(str)
parts = str.split
arr = []
arr = parts.map do |part|
part.capitalize
end
return arr.join(" ")
end
# one where your loop code logic works
def format_name(str)
parts = str.split
arr = []
parts.map do |part|
arr << "#{part[0].upcase}#{part[1..-1].downcase}"
end
return arr.join(" ")
end
There are numerous other ways this could work. I'll also put the one I prefer if I am using just plain ruby:
def format_name(str)
str.split(' ').map(&:capitalize)
end
You could also read more about the Open Classes concept to put this into the String class of ruby
Also, checkout camelize method if you're using rails.

Ruby TDD and Rspec

Im new to testing in ruby with Rspec. I'm just wanting to write a simple test to see if the below code works. Im not sure how to do it. The code returns an acronym of a given string. thanks
def acronym(sentence)
first_letters = []
sentence.split.each do |word|
first_letters << word[0]
end
first_letters.join
end
describe "acro method" do
it "returns acronym of words" do
end
end
Define Your Input and Expected Output
The point of TDD is to test expected behavior. To construct a test, you must define both your fixture (a known input value) and your expectation (the output you expect your method to produce given a known input value). You then compare the results of your spec with a suitable matcher. For example:
def acronym(sentence)
first_letters = []
sentence.split.each do |word|
first_letters << word[0]
end
first_letters.join
end
describe "#acronym" do
let(:sentence) { 'A very short sentence.' }
it "returns initial letter of each word" do
expect(acronym sentence).to eq('Avss')
end
end
When you run the spec in document format, it should read fairly naturally.
$ rspec --format doc foo_spec.rb
#acronym
returns initial letter of each word
Finished in 0.0017 seconds (files took 0.12358 seconds to load)
1 example, 0 failures
If you change your test's expected output from Avss to avss, then your expectation will fail. A well-written test will give you a useful error like:
Failures:
1) #acronym returns initial letter of each word
Failure/Error: expect(acronym sentence).to eq('avss')
expected: "avss"
got: "Avss"
(compared using ==)
You can then fix your class or method until the desired behavior is achieved.
Use RSpec matchers to check that what your method outputs actually matches what you expect it to do.
https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers
describe "acro method" do
it "returns acronym of words" do
test_sentence = "this is a test acronym"
expected_acronym = "tiata"
expect(acronym(test_sentence)).to eq(expected_acronym)
end
end

Can I incrementally configure the test subject in nexted contexts?

I'm new to Ruby and RSpec, and I'm looking for a way to incrementally configure the test subject in nested contexts. The sort of thing I want to do is illustrated by the following, obviously contrived, example:
describe "Array" do
context "An array" do
let (:array_length) do
a = Array.new
config_array a
return a.length
end
context "with 1 element" do
def config_array(arr)
arr << '1'
end
it "should have length 1" do
expect(array_length).to eq(1)
end
context "and an additional element" do
def config_array(arr)
arr << '1'
arr << '2'
end
it "should have length 2" do
expect(array_length).to eq(2)
end
context "and yet another additional element" do
def config_array(arr)
arr << '1'
arr << '2'
arr << '3'
end
it "should have length 3" do
expect(array_length).to eq(3)
end
end
end
end
end
end
The tests in this example pass, but I'd like to be able to do something like:
# snip
context "and an additional element" do
def config_array(arr)
parent_context_config_array arr
arr << '2'
end
it "should have length 2" do
expect(array_length).to eq(2)
end
context "and yet another additional element" do
def config_array(arr)
parent_context_config_array arr
arr << '3'
end
# snip
Is there a simple way to do this? Obviously the real test subject I'm working with is more complex than this. I'm basically looking for a way to keep the code DRY while adding additional configuration to the test subject in nested contexts.
I would do something like that:
describe 'Array' do
subject { Array.new }
context '#length' do
context 'with 1 element' do
before { subject << '1' }
it 'should eq 1' do
expect(subject.length).to eq 1
end
context 'and an additional element' do
before { subject << '2' }
it 'should eq 2' do
expect(subject.length).to eq 2
end
# ...
end
end
end
end
All the before blocks (shortcut for before(:each)) in the parent contexts of a given it block are going to be evaluated in descending order before the actual it code. Together, that forms what we call an example.
The subject method is simply syntactic sugar around the let method. The first time we call it inside an example the block that we provided in the subject declaration is evaluated and its result memoized. Therefore any ulterior call to subject in that particular example would return the same object.
We take advantage of that in the second example, the one for the two elements. The first before calls to subject which evaluates Array.new returns the array and memoizes it. Then, the << method modifies the now memoized object, that will be modified again in the second before block so by the time it reaches the expectation will indeed contain two elements.

Undefined method in console?

Here comes another Codecademy question:
The following challenge has been presented.
Define two methods in the editor:
A greeter method that takes a single string parameter, name, and
returns a string greeting that person. (Make sure to use return and
don't use print or puts.)
A by_three? method that takes a single integer parameter, number, and
returns true if that number is evenly divisible by three and false if
not. Remember, it's a Ruby best practice to end method names that
produce boolean values with a question mark.
The code I put in re: was..
def greeter(name)
return "Greet #{name}"
end
def by_three?(x)
if x % 3==0
returns true
else
return false
end
greeter("Brant")
by_three?(6)
The console then gives me the following error:
Did you define your greeter method?
It seems like I have. Am I wrong?
this would be it:
def greeter(name)
"Greet #{name}"
end
def by_three?(x)
x % 3 == 0
end
greeter("Brant") # => "Greet Brant"
by_three?(6) # => true
It looks like you did not add "end" after your else statement. Here you go.
#For the greeter method, i decided to use this format
def greeter(name)
return name
end
greeter("Hello Jane, good morning")
def by_three?(number)
if number % 3 != 1
return true
else
return false
end #Add end here to end your statement
end
by_three?(5)

Implicit return values in Ruby

I am somewhat new to Ruby and although I find it to be a very intuitive language I am having some difficulty understanding how implicit return values behave.
I am working on a small program to grep Tomcat logs and generate pipe-delimited CSV files from the pertinent data. Here is a simplified example that I'm using to generate the lines from a log entry.
class LineMatcher
class << self
def match(line, regex)
output = ""
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
return output
end
end
end
puts LineMatcher.match("00:00:13,207 06/18 INFO stateLogger - TerminationRequest[accountId=AccountId#66679198[accountNumber=0951714636005,srNumber=20]",
/^(\d{2}:\d{2}:\d{2},\d{3}).*?(\d{2}\/\d{2}).*?\[accountNumber=(\d*?),srNumber=(\d*?)\]/)
When I run this code I get back the following, which is what is expected when explicitly returning the value of output.
00:00:13,207|06/18|0951714636005|20
However, if I change LineMatcher to the following and don't explicitly return output:
class LineMatcher
class << self
def match(line, regex)
output = ""
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
end
end
end
Then I get the following result:
00:00:13,207
06/18
0951714636005
20
Obviously, this is not the desired outcome. It feels like I should be able to get rid of the output variable, but it's unclear where the return value is coming from. Also, any other suggestions/improvements for readability are welcome.
Any statement in ruby returns the value of the last evaluated expression.
You need to know the implementation and the behavior of the most used method in order to exactly know how your program will act.
#each returns the collection you iterated on. That said, the following code will return the value of line.scan(regexp).
line.scan(regex).each do |matched|
output << matched.join("|") << "\n"
end
If you want to return the result of the execution, you can use map, which works as each but returns the modified collection.
class LineMatcher
class << self
def match(line, regex)
line.scan(regex).map do |matched|
matched.join("|")
end.join("\n") # remember the final join
end
end
end
There are several useful methods you can use depending on your very specific case. In this one you might want to use inject unless the number of results returned by scan is high (working on arrays then merging them is more efficient than working on a single string).
class LineMatcher
class << self
def match(line, regex)
line.scan(regex).inject("") do |output, matched|
output << matched.join("|") << "\n"
end
end
end
end
In ruby the return value of a method is the value returned by the last statement. You can opt to have an explicit return too.
In your example, the first snippet returns the string output. The second snippet however returns the value returned by the each method (which is now the last stmt), which turns out to be an array of matches.
irb(main):014:0> "StackOverflow Meta".scan(/[aeiou]\w/).each do |match|
irb(main):015:1* s << match
irb(main):016:1> end
=> ["ac", "er", "ow", "et"]
Update: However that still doesn't explain your output on a single line. I think it's a formatting error, it should print each of the matches on a different line because that's how puts prints an array. A little code can explain it better than me..
irb(main):003:0> one_to_three = (1..3).to_a
=> [1, 2, 3]
irb(main):004:0> puts one_to_three
1
2
3
=> nil
Personally I find your method with the explicit return more readable (in this case)

Resources