Mock system call in ruby - ruby

Know of a way to mock %[]? I'm writing tests for code that makes a few system calls, for example:
def log(file)
%x[git log #{file}]
end
and would like to avoid actually executing system calls while testing this method. Ideally I'd like to mock %x[..] and assert that the correct shell command is passed to it.

%x{…} is Ruby built-in syntax that will actually call Kernel method Backtick (`). So you can redefine that method. As backtick method returns the standard output of running cmd in a subshell, your redefined method should return something similar to that ,for example, a string.
module Kernel
def `(cmd)
"call #{cmd}"
end
end
puts %x(ls)
puts `ls`
# output
# call ls
# call ls

Using Mocha, if you want to mock to following class:
class Test
def method_under_test
system "echo 'Hello World!"
`ls -l`
end
end
your test would look something like:
def test_method_under_test
Test.any_instance.expects(:system).with("echo 'Hello World!'").returns('Hello World!').once
Test.any_instance.expects(:`).with("ls -l").once
end
This works because every object inherits methods like system and ` from the Kernel object.

I don't know of a way to mock a module, I'm afraid. With Mocha at least, Kernel.expects doesn't help. You could always wrap the calling in a class and mock that, something like this:
require 'test/unit'
require 'mocha'
class SystemCaller
def self.call(cmd)
system cmd
end
end
class TestMockingSystem < Test::Unit::TestCase
def test_mocked_out_system_call
SystemCaller.expects(:call).with('dir')
SystemCaller.call "dir"
end
end
which gives me what I'd hope for:
Started
.
Finished in 0.0 seconds.
1 tests, 1 assertions, 0 failures, 0 errors

How about logging it to a text file, or outputting it to your console?
def log(file)
puts "git log #{file}"
end

Can't you just ovverride the function with a method that returns true when it gets the command?

Related

How to write rspec with mocking

I want to write an rspec test by mocking this method. Should I break this method up, as it doing multiple things?
require 'yaml'
require_relative 'checkerror'
class Operations
    def initialize
        #check
  end
    def result (result_log: File.new('result.txt', 'a+'))
      if #check.errors.empty?
 result_log.write("#{#check.checker.file_path} :: No offensenses detected\n")
#checker is instance of CheckError class
  puts "#{#check.checker.file_path} :: No offensenses detected\n"
      else
        #check.errors.uniq.each do |err|  puts "#{#check.checker.file_path} : #{err}\n"
 result_log.write("#{#check.checker.file_path} : #{err}\n")
 end
end
result_log.close
end
  end
end
If #check.errors need to be stuubed with a value and check the execution block.
It's going to be awkward mocking the f object in your current implementation, due to this line:
f = File.new('result.txt', 'a+')
You'd need to write something weird in the rspec test, like:
allow(File).to receive(:new).with('result.txt', 'a+').and_return(mock_file)
So instead, I'd recommend using dependency injection to pass the file into the method. For example:
def check_result(results_log: File.new('result.txt', 'a+'))
if #errors.empty?
# ...
end
Now, your rspec test can look something like this:
let(:results_log) { Tempfile.new }
it "prints errors to log file" do
wharever_this_object_is_called.check_result(result_log: results_log)
expect(result_log.read).to eq("checker_file_path.txt :: No offences detected\n")
end

Ruby minitest assert_output syntax

I am new to minitest and still new to ruby and really tired of trying to google this question without result. I would be really grateful for help:
What is the exact syntax of assert_output in ruby minitest?
All I find on github or elsewhere seems to use parentheses. Yet, I get an error message when I don't use a block with assert_output, which makes sense as the definition of this method contains a yield statement.
But I cannot make it work, whatever I try.
testclass.rb
class TestClass
def output
puts 'hey'
end
end
test_test.rb
require 'minitest/spec'
require 'minitest/autorun'
require_relative 'testclass'
class TestTestClass < MiniTest::Unit::TestCase
def setup
#test = TestClass.new
end
def output_produces_output
assert_output( stdout = 'hey' ) { #test.output}
end
end
What I get is:
Finished tests in 0.000000s, NaN tests/s, NaN assertions
0 tests, 0 assertions, 0 failures, 0 errors, 0 skips
What am I doing wrong?
It must be something totally obvious, but I cannot figure it out.
Thanks for your help.
In order for your test method to run, the method name needs to start with test_. Also, the way assert_output works is that the block will write to stdout/stderr, and the arguments will be checked if they match stdout/stderr. The easiest way to check this IMO is to pass in a regexp. So this is how I would write that test:
class TestTestClass < MiniTest::Unit::TestCase
def setup
#test = TestClass.new
end
def test_output_produces_output
assert_output(/hey/) { #test.output}
end
end

Rspec 3.0 How to mock a method replacing the parameter but with no return value?

I've searched a lot and just cannot figure this out although it seems basic. Here's a way simplified example of what I want to do.
Create a simple method that does something but doesn't return anything, such as:
class Test
def test_method(param)
puts param
end
test_method("hello")
end
But in my rspec test I need to pass a different parameter, such as "goodbye" instead of "hello." I know this has to do with stubs and mocks, and I've looking over the documentation but can't figure it out: https://relishapp.com/rspec/rspec-mocks/v/3-0/docs/method-stubs
If I do:
#test = Test.new
allow(#test).to_receive(:test_method).with("goodbye")
it tells me to stub out a default value but I can't figure out how to do it correctly.
Error message:
received :test_method with unexpected arguments
expected: ("hello")
got: ("goodbye")
Please stub a default value first if message might be received with other args as well.
I am using rspec 3.0, and calling something like
#test.stub(:test_method)
is not allowed.
How to set a default value that is explained at
and_call_original can configure a default response that can be overriden for specific args
require 'calculator'
RSpec.describe "and_call_original" do
it "can be overriden for specific arguments using #with" do
allow(Calculator).to receive(:add).and_call_original
allow(Calculator).to receive(:add).with(2, 3).and_return(-5)
expect(Calculator.add(2, 2)).to eq(4)
expect(Calculator.add(2, 3)).to eq(-5)
end
end
Source where I came to know about that can be found at https://makandracards.com/makandra/30543-rspec-only-stub-a-method-when-a-particular-argument-is-passed
For your example, since you don't need to test the actual result of test_method, only that puts gets called in it passing in param, I would just test by setting up the expectation and running the method:
class Test
def test_method(param)
puts param
end
end
describe Test do
let(:test) { Test.new }
it 'says hello via expectation' do
expect(test).to receive(:puts).with('hello')
test.test_method('hello')
end
it 'says goodbye via expectation' do
expect(test).to receive(:puts).with('goodbye')
test.test_method('goodbye')
end
end
What it seems you're attempting to do is set up a test spy on the method, but then I think you're setting up the method stub one level too high (on test_method itself instead of the call to puts inside test_method). If you put the stub on the call to puts, your tests should pass:
describe Test do
let(:test) { Test.new }
it 'says hello using a test spy' do
allow(test).to receive(:puts).with('hello')
test.test_method('hello')
expect(test).to have_received(:puts).with('hello')
end
it 'says goodbye using a test spy' do
allow(test).to receive(:puts).with('goodbye')
test.test_method('goodbye')
expect(test).to have_received(:puts).with('goodbye')
end
end

Is there a way to mock/stub "puts" in Rails

I am printing some custom messages in my application using the puts command. However, I do not want these to be appearing in my Test Output. So, I tried a way to stub puts as shown below. But it still outputs my messages. What am I doing wrong ?
stubs(:puts).returns("") #Did not work out
Object.stubs(:puts).returns("") #Did not work out either
puts.stubs.returns "" #Not working as well
Kernel.stubs(:puts).returns "" #No luck
I am using Test::Unit
You probably need to stub it on the actual instance that calls puts. E.g. if you're calling puts in an instance method of a User class, try:
user = User.new
user.stubs(:puts)
user.some_method_that_calls_puts
This similarly applies to when you're trying to test puts in the top-level execution scope:
self.stubs(:puts)
What I would do is define a custom log method (that essentially calls puts for now) which you can mock or silence in test quite easily.
This also gives you the option later to do more with it, like log to a file.
edit: Or if you really want to stub puts, and you are calling it inside an instance method for example, you can just stub puts on the instance of that class.
Using Rails 5 + Mocha: $stdout.stubs(puts: '')
So the comments to the original post point to the answer:
Kernel.send(:define_method, :puts) { |*args| "" }
Instead of silencing all output, I would only silence output from the the particular objects that are putsing during your tests.
class TestClass
def some_method
...
puts "something"
end
end
it "should do something expected" do
TestClass.send(:define_method, :puts) { |*args| "" }
test_class.some_method.should == "abc123"
end

Before/After Suite when using Ruby MiniTest

Is there an alternative to RSpec's before(:suite) and after(:suite) in MiniTest?
I suspect that a custom test runner is in order, however I cannot imagine it is not a common requirement, so somebody has probably implemented in. :-)
There are setup() and teardown() methods available. The documentation also lists before() and after() as being available.
Edit: Are you looking to run something before each test or before or after the whole suite is finished?
As noted above in Caley's answer and comments, MiniTest::Unit contains the function after_tests. There is no before_tests or equivalent, but any code in your minitest_helper.rb file should be run before the test suite, so that will do the office of such a function.
Caveat: Still relatively new at Ruby, and very new at Minitest, so if I'm wrong, please correct me! :-)
To get this to work with the current version of Minitest (5.0.6) you need to require 'minitest' and use Minitest.after_run { ... }.
warn "MiniTest::Unit.after_tests is now Minitest.after_run. ..."
https://github.com/seattlerb/minitest/blob/master/lib/minitest.rb
https://github.com/seattlerb/minitest/blob/master/lib/minitest/unit.rb
To run code before each test, use before. You're operating here in the context of an instance, possibly of a class generated implicitly by describe, so instance variables set in before are accessible in each test (e.g. inside an it block).
To run code before all tests, simply wrap the tests in a class, a subclass of MiniTest::Spec or whatever; now, before the tests themselves, you can create a class or module, set class variables, call a class method, etc., and all of that will be available in all tests.
Example:
require "minitest/autorun"
class MySpec < MiniTest::Spec
class MyClass
end
def self.prepare
puts "once"
##prepared = "prepared"
##count = 0
end
prepare
before do
puts "before each test"
#local_count = (##count += 1)
end
describe "whatever" do
it "first" do
p MyClass
p ##prepared
p #local_count
end
it "second" do
p MyClass
p ##prepared
p #local_count
end
end
end
Here's the output, along with my comments in braces explaining what each line of the output proves:
once [this code, a class method, runs once before all tests]
Run options: --seed 29618 [now the tests are about to run]
# Running tests:
before each test [the before block runs before each test]
MySpec::MyClass [the class we created earlier is visible in each test]
"prepared" [the class variable we set earlier is visible in each test]
1 [the instance variable from the before block is visible in each test]
before each test [the before block runs before each test]
MySpec::MyClass [the class we created earlier is visible in each test]
"prepared" [the class variable we set earlier is visible in each test]
2 [the instance variable from the before block is visible each test]
(Note that I do not mean this output to imply any guarantee about the order in which tests will run.)
Another approach is to use the existing before but wrap code to run only once in a class variable flag. Example:
class MySpec < MiniTest::Spec
##flag = nil
before do
unless ##flag
# do stuff here that is to be done only once
##flag = true
end
# do stuff here that is to be done every time
end
# ... tests go here
end
One simple way to do this is to write a guarded class method, and then call that in a begin.
A Minitest::Spec example:
describe "my stuff" do
def self.run_setup_code
if #before_flag.nil?
puts "Running the setup code"
#before_flag = true
end
end
before do
self.class.run_setup_code
end
it "will only run the setup code once" do
assert_equal 1, 1
end
it "really only ran it once" do
assert_equal 1,1
end
end
...to get
Run options: --seed 11380
# Running:
Running the setup code
..
Finished in 0.001334s, 1499.2504 runs/s, 1499.2504 assertions/s.
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips
You can just place the code outside of the class.
This is what I do to have a banner.
require 'selenium-webdriver'
require 'minitest/test'
require 'minitest/autorun'
class InstanceTest < Minitest::Test
def setup
url = ARGV.first
#url = self.validate_instance(url)
#driver = Selenium::WebDriver.for :firefox
end
Nice thing about minitest is its flexibility. I've been using a custom MiniTest Runner with a +before_suite+ callback. Something like in this example - Ruby Minitest: Suite- or Class- level setup?
And then tell minitest to use the custom runner
MiniTest::Unit.runner = MiniTestSuite::Unit.new
You can also add an after test callback by updating your test_helper.rb (or spec_helper.rb) like this
# test_helper.rb
class MyTest < Minitest::Unit
after_tests do
# ... after test code
end
end

Resources