I have the following piece of code which I want to test using minitest:
def write(text, fname)
File.open(fname, 'w') do |f|
f << text
end
end
I wanted to mock the file using StringIO, so I did:
class TestWrite << Minitest::Test
def test_write
# arrange
m_file = StringIO.new
mock_open = Minitest::Mock.new
mock_open.expect :call, m_file, ['fname', 'w']
# act
File.stub :open, mock_open do
write('hello!', 'fname')
end
# assert
mock_open.verify # successful assert!
assert_equal 'hello!', m_file.string # failure :(
end
The second assert seem to fail because the code inside the do block of File.open never runs. Any idea what I'm doing wrong?
Related
Let's say I have a class Test
class Test
def initialize()
puts "cool"
end
end
Is there a way to extend initialize class somehow and execute some method in it?
For example I want to:
class Test
def func()
puts "test"
end
end
test = Test.new()
Should output
cool
test
Thanks!
You can define a module containing your extension:
module TestExtension
def initialize
super
puts 'test'
end
end
and then prepend that module to Test:
class Test
def initialize
puts 'cool'
end
end
Test.prepend(TestExtension)
Test.new
# cool
# test
If the code for Test is not under your control, and you want to inject test:
Test.class_eval do
def test
puts "TEST"
end
alias initialize_without_test initialize
# This, if you want the return value of `test` to replace the original's
def initialize(*args, &block)
initialize_without_test(*args, &block)
test
end
# Or this, if you want to keep the return value of original `initialize`
def initialize(*args, &block)
initialize_without_test(*args, &block).tap do
test
end
end
end
Normally, we can do this with Proc objects:
[15] pry(main)> pr = -> { puts "test message" }
=> #<Proc:0x000000057b8a90#(pry):12 (lambda)>
[16] pry(main)> pr.call
test message
=> nil
[17] pry(main)> define_method("test_method", pr)
=> :test_method
[18] pry(main)> test_method
test message
But, what if I have a raw code string and want to load it into a proc? Fake code below:
raw_code = "puts 'test message'"
pr = -> { load(raw_code) } # how to define the `load` method to get this working?
pr.call # => test message
define_method("test_method", pr}
test_method # => test message
Actually, my original problem is how to write a method hook like this:
class TestClass
def test_method
puts url
end
def test_method_a
puts url
end
before :test_method, :test_method_a do
url = __method__.split("_").join("/")
end
end
TestClass.new.test_method # => "test/method"
TestClass.new.test_method_a # => "test/method/a"
My problem is more complicated, this is just a simple example to illustrate the key problem.
how to define the load method to get this working?
By spelling it eval:
raw_code = "puts 'test message'"
pr = -> { eval(raw_code) } # how to define the `load` method to get this working?
pr.call # => test message
define_method("test_method", pr)
test_method # => test message
--output:--
test message
test message
Actually, my original problem is how to write a method hook...
class TestClass
def initialize
#url = %q{__method__.to_s.split("_").join("/")}
end
def test_method
puts(eval #url)
end
def test_method_a
puts(eval #url)
end
end
TestClass.new.test_method # => "test/method"
TestClass.new.test_method_a # => "test/method/a"
--output:--
test/method
test/method/a
Actually, my original problem is how to write a method hook like this:
Module TestClass
def test_method
puts url
end
The problem is that url can never refer to a value outside the def. A def cuts off the visibility of local variables outside the def.
class TestClass
def test_method
puts #url
end
def test_method_a
puts #url
end
def self.before(*method_names, &block)
method_names.each do |method_name|
alias_method "first_#{method_name}", method_name
define_method(method_name, block) #This changes __method__ inside the block from nil to method_name
alias_method "second_#{method_name}", method_name
define_method(method_name) do
send "second_#{method_name}" #This method executes: #url = __method__.to_s.split(...
send "first_#{method_name}" #This method executes: puts #url
end
end
end
before :test_method, :test_method_a do
#url = __method__.to_s.split("_").join("/")
end
end
TestClass.new.test_method # => "test/method"
TestClass.new.test_method_a # => "test/method/a"
--output:--
test/method
test/method/a
Short version: load loads code from a file. If you want to run code that you already have in a string, you can use eval, or one of it's friends, class_eval or instance_eval.
If you do end up using eval, however, you need to be very careful so that you won't accidentally run code that could delete your files, install malware or whatever.
Longer version: For a working version (in ruby 2.2.3) with load, you would need to put your TestClass class in a file:
class TestClass
def test_method
puts "OHAI"
end
end
Let's save this class in a file called "test_class.rb".
With that, the following should just work:
pr = -> { load(File.join(__dir__, "test_class.rb")) }
pr.call
TestClass.new.test_method
This will not solve your "original problem", but it should give you a little better understanding on how load works.
My own solution and more detail shown here after reading reply:
Maybe there is a lot of confusion about why does my problem method hook matter with load raw source code to Proc instance.
And also, I've solved my problem,now.
So,let me explain the whole thing in detail:
1, my original problem comes from that I need to extract a bunch of duplicated code at head of some methods of a module just like this:
module TestClass
extend self
def test_method
url = __method__.to_s.split("_").join("/") # duplicated code,need to extract
puts url
end
def test_method_a
url = __method__.to_s.split("_").join("/") # duplicated code, need to extract
puts url
end
2, Then, after a lot of thinking, I come up with an idea, that's get test_method test_method_a's source code, and modify the source code by adding url = __method__.to_s.split("_").join("/") at the head of it, then redefine the method with new code.
3, After lot of hack, I failed with using eval and then, post here asking help.
4, After reading reply, I make sure eval is definitely what I want.
5, Finally, I succeed, code show here:
module TestClass
extend self
def test_method
puts url
end
def test_method_a
puts url
end
[ :test_method, :test_method_a ].each do |name|
method_source_code_ar = instance_method(name).source.split("\n")
method_source_code_ar.insert(1, 'url = __method__.to_s.split("_").join("/")')
method_source_code = method_source_code_ar[1..-2].join("\n")
define_method(name, -> { eval(method_source_code) })
end
end
TestClass.test_method # => test/method
TestClass.test_method_a # => test/method/a
6, More concise code version show:
module TestClass
extend self
def test_method
puts url
end
def test_method_a
puts url
end
[ :test_method, :test_method_a ].each do |name|
method_source_code_ar = instance_method(name).source.split("\n")
method_source_code_ar.insert(1, 'url = __method__.to_s.split("_").join("/")')
method_source_code = method_source_code_ar.join("\n")
eval(method_source_code)
end
end
7, As eval,for me, I think the key to understand it is,thinking not as if you're in writing mode, but as if code was running line by line until reaching the eval line code, by that time, what do you want the code to do? Sure, just eval the method_source_code string.
I have a section in my code which polls a queue for a message and then acts on it depending on the message type:
#queue = Foo::Queue.new
loop do
#queue.poll do |message|
if message[:task] == TAKEACTION
result = takeaction(message)
#queue.send_result(result, message)
end
#queue.pong(message) if message[:task] == PING
end
end
How do I set up a test to supply a single message and verify that the #queue acts on it as I expect?
I have seen very little about testing blocks in minitest, and haven't found anything in ruby regarding breaking out of infinite loops, though I found one idea in python where you set up the second run to throw an exception.
Can any ruby / minitest gurus help?
For minitest using a stub will work. The example below is self contained and can run on its own. You can send an exception as a lambda with a stub to break the infinite loop and continue testing.
# class receiving the messages from class being tested
class Obj
def method_if_true
# do some stuff
return 'anything that is true'
end
def method_if_false
# do some stuff
false
end
end
# class to be tested
class TestingLoop
def initialize(args)
#obj = args.fetch(:object, Obj.new)
#bool = args[:bool]
end
def looping
loop do
if #bool
#obj.method_if_true
# #bool is true
elsif !#bool
#obj.method_if_false
# #bool is false
end
end
end
end
require 'minitest/autorun'
# class that tests
class TestTestingLoop < MiniTest::Test
def setup
#obj = Obj.new
#raises_exception = lambda { raise RuntimeError.new }
# could use the '->' syntax instead of the word lambda
end
def test_sends_correct_method_when_true
#obj.stub :method_if_true, #raises_exception do
testing_loop = TestingLoop.new({object: #obj, bool: true})
assert_raises(RuntimeError) { testing_loop.looping }
end
end
def test_sends_correct_method_when_false
#obj.stub :method_if_false, #raises_exception do
testing_loop = TestingLoop.new({object: #obj, bool: false})
assert_raises(RuntimeError) { testing_loop.looping }
end
end
end
I'm trying to run some tests on my code, but I'm running into a problem. For a very simple line of code, I get a weird error message. This test is to make sure my server can receive info from one of my clients.
The tests and file run fine without this line:
client_1.#socket.puts("This gives an error.")
Including that bit of code gives this error:
macowner:WAR3 macowner$ ruby ServerTests1.rb
ServerTests1.rb:160: syntax error, unexpected tIVAR, expecting '('
client_1.#socket.puts("Output for Server to receive.") #Error
^
Help is greatly appreciated. I get this error fairly frequently, but I have no idea what it means (even with searching for it).
enter code here
require 'minitest/autorun'
require 'socket'
require_relative 'WarGame_Class.rb'
require_relative 'ModifiedPlayer_Class.rb'
require_relative 'DeckClass.rb'
class WarServer
def initialize(host, port)
#socket_server = TCPServer.new(host, port)
#players = [Player.new, Player.new]
#deck = CardDeck.new
#deck.deal_cards(#players[0].cards, #players[1].cards)
game = WarGame.new
#clients = {} # keys are sockets, values are players
end
def client_keys(key)
#clients.keys[key] # this should work
end
def input #input reader function
#input
end
def close
#socket_server.close
end
def capture_input ##input client to get what they wrote
#input = #clients.keys[0].read_nonblock(1000) # arbitrary max number of bytes
end
def accept_client
#Hash here to link client to player? (or game?)
client = #socket_server.accept
#clients[client] = #players[#clients.size]
# puts "clients key 0: #{#clients.keys[0]}"
puts
# puts "clients values: #{#clients.values}"
if #clients.size == 2
start_game#####################!!!! Starts game if two clients can put client messages in start game
end
end
def start_game ##############!!!
#clients.keys[0].puts "Welcome to War. Please press enter to play your card"
#clients.keys[1].puts "Welcome to War. Please press enter to play your card"
end
end
class MockWarClient
def initialize
#socket = TCPSocket.new('localhost', 2012)
end
def output
#output
end
def input
#input
end
def capture_output #need to add (socket)? How else read from specific socket?
#output = #socket.read_nonblock(1000) # arbitrary max number of bytes
rescue
#output = "capture_output error."
end
def write_input
#input = #war_server.client_keys.write_nonblock(1000)
end
end
class WarServerTest < MiniTest::Unit::TestCase
def setup #This would be like our INITIALIZE Function
#anything is available through out all tests (i.e., instance vars)
#war_server = WarServer.new('localhost', 2012)
end
def teardown
#war_server.close
end
def test_server_capture_output_from_client
client_1 = MockWarClient.new
#war_server.accept_client
client_2 = MockWarClient.new
#war_server.accept_client
client_1.#socket.puts("Output for Server to receive.") #Line I need to fix
#SOCKETforSERVER
#clien_n.SOCKETforSERVER.puts''
#Replace puts with write_nonblock, perhaps
end
end
If you're trying to access the #socket instance variable then you just need another accessor method in MockWarClient:
def socket
#socket
end
and the say client_1.socket.puts(...) to use it. You could also use attr_reader to simplify things:
class MockWarClient
attr_reader :socket, :input, :output
def initialize
#socket = TCPSocket.new('localhost', 2012)
end
def capture_output #need to add (socket)? How else read from specific socket?
#output = #socket.read_nonblock(1000) # arbitrary max number of bytes
rescue
#output = "capture_output error."
end
def write_input
#input = #war_server.client_keys.write_nonblock(1000)
end
end
That will create the three accessor methods for you.
You cannot call a method named #socket in a usual way as you are trying in client_1.#socket. That method name is very unusual because it is confusing with an instance variable, and it is doubtful that you actually have such method, but if you do, then the way to call such method is:
client_1.send(:#socket)
I am trying to write fails and errors that occur in the test to a log file, so that they don't appear on-screen, but it appears as though errors and assert failures write to STDOUT instead of STDERR. I have been unable to find information on how to redirect this output after hours of googling, and would greatly appreciate help.
Why should the errors not be on stdout?
Up to now I have no prepared solution to suppress the output for specific errors.
If you accept the unchanged output, I have a solution to store errors and failures in a file. It will be no problem to create a 2nd file for Notifications...
gem 'test-unit'
require 'test/unit'
module Test
module Unit
class TestSuite
alias :old_run :run
def run(result, &progress_block)
old_run(result, &progress_block)
File.open('test.log', 'w'){|f|
result.faults.each{|err|
case err
when Test::Unit::Error, Test::Unit::Failure
f << err.long_display
f << "\n===========\n"
#not in log file
when Test::Unit::Pending, Test::Unit::Notification, Test::Unit::Omission
end
}
}
end
end
end
end
class MyTest < Test::Unit::TestCase
def test_1()
assert_equal( 3, 1+1) #failure
end
def test_2()
1 / 0 #force an error
end
def test_3()
notify 'sss'
end
def test_4()
pend "MeineKlasse.new"
end
def test_5
omit 'aaa' if RUBY_VERSION == '1.9.2'
end
def test_5
assert_in_delta( 0.1, 0.00001, 1.0/10)
end
end
Have you tried redirecting the output to StringIO, to write it to a logfile later?
original_stdout = $stdout
original_stderr = $stderr
fake_stdout = StringIO.new
fake_stderr = StringIO.new
$stdout = fake_stdout
$stderr = fake_stderr
Then after you've ran the tests:
$stdout = original_stdout
$stderr = original_stderr
#stdout = fake_stdout.string
#stderr = fake_stderr.string
I'm not really sure this will work though...
I hope I understood yor question correct.
Do you mean something like this:
gem 'test-unit'
require 'test/unit'
class StdOutLogger < IO
def initialize(*)
super
#file = File.open('log.txt', 'w')
#stdout = true
end
#write to stdout and file
def write(s)
$stdout << s
#file << s
end
end
STDOUT = StdOutLogger.new(1)
class MyTest < Test::Unit::TestCase
def test_1()
assert_equal( 2, 1+1)
assert_equal( 2, 4/2)
assert_equal( 1, 3/2)
assert_equal( 1.5, 3/2.0)
end
end
But I would recommend to copy stdout on operation system level
ruby mytest.rb > log.txt
Here is a version to skip between stdout and file output. The output switch is only a interim solution - perhaps it can be made better.
class StdOutLogger < IO
def initialize(*)
super
#file = File.open('log.txt', 'w')
#stdout = true
end
def write(s)
case s
when /\AError:/
#stdout = false #change to file
when /\A(Pending):/
#stdout = true #change to file
end
if #stdout
$stdout << s
else
#file << s
end
end
end
STDOUT = StdOutLogger.new(1)