I am trying to mock file read with the help of StringIO in Ruby.
The following is my test and next to that is my method in the main class.
def test_get_symbols_from_StringIO_file
s = StringIO.new("YHOO,141414")
assert_equal(["YHOO,141414"], s.readlines)
end
def get_symbols_from_file (file_name)
IO.readlines(file_name, ',')
end
I want to know if this is the way we mock the file read and also I would like to know if there is some other method to mock the method in the class rather than doing assert equal with contents.
Your method get_symbols_from_file is never called in the test. You're just testing that StringIO#readlines works, i.e.:
StringIO.new("YHOO,141414").readlines == ["YHOO,141414"] #=> true
If you want to use a StringIO instance as a placeholder for your file, you have to change your method to take a File instance rather than a file name:
def get_symbols_from_file(file)
file.readlines(',')
end
Both, File and StringIO instances respond to readlines, so the above implementation can handle both:
def test_get_symbols_from_file
s = StringIO.new("YHOO,141414")
assert_equal(["YHOO,141414"], get_symbols_from_file(s))
end
This test however fails: readlines includes the line separator, so it returns an array with two elements "YHOO," (note the comma) and "141414". You are expecting an array with one element "YHOO,141414".
Maybe you're looking for something like this:
def test_get_symbols_from_file
s = StringIO.new("YHOO,141414")
assert_equal(["YHOO", "141414"], get_symbols_from_file(s))
end
def get_symbols_from_file(file)
file.read.split(',')
end
If you really want to use IO::readlines you could create a Tempfile:
require 'tempfile'
def test_get_symbols_from_file
Tempfile.open("foo") { |f|
f.write "YHOO,141414"
f.close
assert_equal(["YHOO", "141414"], get_symbols_from_file(f.path))
}
end
Related
I have a problem with Object-Oriented Project Hangman - serialization part. I saved my code in serialize method, but when I try to unserialize it, I have a problem with it. I see all components of classes in the YAML file and when I try to reach them with code, I can't do it. I don't know where the problem is. Here is my serialize method and deserialize method.
def serialize
file = File.open('game.yml', 'w') do |f|
YAML.dump(self, f)
end
file.close
exit
end
def deserialize
File.open('game.yml', 'r') do |f|
p YAML.load(f)
end
# p file['word']
end
If you want to see my all codes, here is my GitHub and Repl
Repl : https://replit.com/#Burakkepuc/Hangman#main.rb
GitHub : https://github.com/Burakkepuc/Hangman
I think it's actually working fine.
YAML.load returns an different instance of Game. You can call methods on that instance, but you don't have any access to the properties.
def deserialize
File.open('game.yml', 'r') do |f|
loaded_game = YAML.load(f)
puts loaded_game.length # fails with method not found
end
# p file['word']
end
add a reader to you Game class and then the above will report the value of the length in the newly load instance of Game.
attr_reader :length
I am not sure what you want to do with the loaded game, but perhaps you simply want to call guess on it?
def deserialize
File.open('game.yml', 'r') do |f|
loaded_game = YAML.load(f)
loaded_game.guess
end
end
Consider the following code in two files:
create_object.rb
def create_object
method = 'new'
Object.send(method)
end
debug.rb
require_relative './create_object'
def foo
object = create_object
bar(object)
end
def bar(object)
# history_of_life - some method that returns object info
puts object.history_of_life
end
foo
Run ruby debug.rb. I expect history_of_life method returns something like this:
<Object:0x007f874b083430> initialized in create_object.rb:3
Are there tools like pry-stacktrace to determine the place in the code where the Object was initialized?
It’s obviously off by default due to enormous footprint, but one might achieve more-or-less desired behaviour with ObjectSpace#allocation_class_path.
In general, ObjectSpace tweaks are your friends in tasks like this one on the top (ruby) abstraction level.
ObjectSpace.allocation_sourcefile returns the source file origin from the given object. Thanks for Aleksei Matiushkin advice.
debug.rb
require 'objspace'
require_relative './create_object'
def foo
object = create_object
bar(object)
end
def bar(object)
puts ObjectSpace.allocation_sourcefile(object)
end
ObjectSpace.trace_object_allocations do
foo
end
output
/home/user/pro/test/create_object.rb
I want to know, what method calls another method (I'm just trying to create simple expect("string").to eq("string") model (just like in RSpect, but more easier).
But i get "main", what is that? (I see that "main" for first time)
public
def expect(message)
message.to_s
end
def to
caller_method = caller_locations.first.label
puts caller_method
end
expect("test").to #=> <main>
#what output i expected:
expect("test").to #=> expect
My goal:
#first i need to do something like that:
expect("test").to eq("test") #=> true
#final must look like this:
expect(expect("test").to eq("test")).to eq(true) #=> true
I would recommend against using caller_method in this case. Rather, make a class whose methods return self - that way they will be chainable:
module Expectation
attr_accessor :caller_method
def expect(arg)
self.caller_method = "expect"
self
end
def to
caller_method
end
end
include Expectation
expect("foo").to
# => "expect"
Obviously this is only a starting point, and this doesn't actually do any comparisons / validations yet. But hopefully you can understand this pattern. The key thing is returning self to make a chainable API, and storing internal state using something like attr_accessor
I need to monkey patch some code and i was wondering if possible to edit a single line ( specific method variable assignment ) within super before calling it ?
class Test_class
def test
res = "AAAAAA"
puts res
end
end
module TestExtensions
def test
# do some Ruby magic here to change res = "BBBBB" ?
super
end
end
class Test_class
prepend TestExtensions
end
Test_class.new.test
---
Output is: AAAAAA
So in this silly example, can i edit the value of res and change it before calling super ? In the original class method i want to patch, I only need to change a single line, from maybe 20 lines of code in the method. Instead of copying the entire method and making a one line change i was hoping to just edit the specific line before calling super.
Update Aug 20th:
Below is the exact code i am trying to money patch.
https://github.com/chef/chef/blob/master/lib/chef/provider/remote_directory.rb#L206
Would like to change only line
res = Chef::Resource::CookbookFile.new(target_path, run_context)
to be instead
res = Chef::Resource::Template.new(target_path, run_context)
If i can not only replace this single line in the super method, maybe i can temporarily alias Chef::Resource::CookbookFile = Chef::Resource::Template so when calling super it returns the correct object, but i don't know if this is possible either.
Would like to change only line
res = Chef::Resource::CookbookFile.new(target_path, run_context)
to be instead
res = Chef::Resource::Template.new(target_path, run_context)
[...] I was just wondering if there was a more ninja Ruby way [...]
Here's a super dirty hack. Assuming that line is the only reference to Chef::Resource::CookbookFile, you could (I don't say you should) define a constant with the same name under the receiver referencing the replacement class:
class Chef
class Provider
class RemoteDirectory
class Chef
class Resource
CookbookFile = ::Chef::Resource::Template
end
end
end
end
end
Now Chef::Resource::CookbookFile within Chef::Provider::RemoteDirectory will resolve to the new constant which returns ::Chef::Resource::Template, so
res = Chef::Resource::CookbookFile.new(target_path, run_context)
effectively becomes:
res = ::Chef::Resource::Template.new(target_path, run_context)
I have a function let's say A whose output and functionality I have to test, A calls another function B which takes a lot of time to compute the output. So I am trying to use stubs to mimic all the values that B returns.
def A
#do something
output = B
#do something with output
end
Now the test files
describe "check what A returns" do
ClassName.stub(:B) do
[0, 1]
end
test_values = TestClass.A(input parameters)
#checks on test values
end
My aim is to pass the expected output of B to function A. I am using RSpec. How do I go about it?
With RSpec you can do:
allow(ClassName).to receive(:B).and_return([1,2,3,4,5])
After this you can call B function and it will return [1,2,3,4,5]
You can find more info at RSpec documentation: https://relishapp.com/rspec/rspec-mocks/v/3-4/docs/configuring-responses/returning-a-value
I've attempted to write some classes and test cases for what it seems like you want to test. The key here is to use allow to stub out the return value for a method.
Just note here that I've changed the methods in your class to be class methods to fit what seems to be your test case, but you can obviously change them back to instance methods to fit your purpose. Also, accepted Ruby style is to have lowercase method names.
class ClassName
def self.B
# returns something that we're going to stub out
end
end
class TestClass
def self.A
# do something
output = ClassName.B
# do something with output
# eg in this case, add a value to it
output << 2
end
end
describe TestClass do
describe '.A' do
before do
allow(ClassName).to receive(:B).and_return([0, 1])
end
it 'does something with output' do
expect(described_class.A).to eq([0, 1, 2])
end
end
end
There's ways as mentioned in other posts but I'll give you another: you might want to make that dependency explicit.
Here's how it could look like:
# test_class.rb
class TestClass
# The default will be automatically setup to be an object of type ClassName
def initialize(some_collaborator: ClassName.new)
self.some_collaborator = some_collaborator # Some people will probably also insert some guard (to make sure it responds to `b`
end
def a
# your code calling `some_collaborator.b`
end
private
attr_accessor :some_collaborator
end
# test_class_spec.rb
describe TestClass do
let(:stub_b) { stub("Some instance of ClassName", b: [...] }
subject { TestClass.new(some_collaborator: stub_b) }
it "whatever" do
expect(subject.a).to ...
end
end
The default collaborator should be a sensible default (and if you can't instantiate it there's ways to encapsulate it anyways). Not only it will be easier to read, but it will be easier to maintain.