Before/After Suite when using Ruby MiniTest
Asked Answered
A

8

47

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. :-)

Achondrite answered 4/5, 2011 at 9:43 Comment(0)
B
31

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?

Billy answered 4/5, 2011 at 13:11 Comment(9)
setup/teardown and before/after run after each test. I need to run something before and after all tests.Achondrite
Try this Nickolay -- after_tests()Billy
Thanks, Caley! I don't know how I missed it. :-)Achondrite
nothing for before_tests though, dammit. I can't believe this still doesn't exist outside of rspec after all these years.Tamathatamaulipas
Update to my comment on May 4, 2011 -- The new documentation appears to be here. The first method listed shows after_tests()Billy
Github issue for a before(all) method to run once per TestCase: github.com/seattlerb/minitest/issues/61 and article related to this topic: chriskottom.com/blog/2014/10/…Oakes
See this answer for running code after the whole suite is finished with Minitest > 5.0Osteomalacia
@CaleyWoods after_tests is now replaced by after_runMelisent
Refer to Gert's answer for before all functionality.Quatrefoil
S
25

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! :-)

Stylite answered 19/1, 2012 at 10:9 Comment(2)
I believe this assumes that you load your test_helper/mintest_helper. If you use require then it will only load once and thus only run onceMislead
Well... Yeah. Exactly. It'll run once, before the tests begin.Stclair
C
21

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

Cotsen answered 24/8, 2013 at 19:47 Comment(0)
B
6

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
Brigittebriley answered 18/9, 2012 at 21:31 Comment(2)
A warning to anyone using the prepare idiom above, as I did, in Rails to set up some database records. If you have even a blank file in the test/fixtures directory, anything you put in the database through prepare will get wiped by fixture setup. Because the variables themselves are still present, weirdness will ensue.Nuclei
(Of course, really one should use fixtures for this anyway, I was being naughty).Nuclei
C
4

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
Constitutionality answered 24/9, 2014 at 12:38 Comment(1)
Nice approach. Curious: why not just hook Ruby's initialize method instead?Dup
P
1

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
Potbellied answered 2/6, 2015 at 21:11 Comment(1)
I voted this answer up but BEWARE, placing code outside the class only works for code that doesn't affect the database. All code outside the class is executed outside the transaction and will clutter the test database if there are any activerecord operations.Nunn
T
0

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
Tinea answered 24/7, 2012 at 7:7 Comment(0)
V
0

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
Virtuosic answered 16/7, 2014 at 13:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.