yegor256
yegor256

Reputation: 105213

How to make sure each Minitest unit test is fast enough?

I have a large amount of Minitest unit tests (methods), over 300. They all take some time, from a few milliseconds to a few seconds. Some of them hang up, sporadically. I can't understand which one and when.

I want to apply Timeout to each of them, to make sure anyone fails if it takes longer than, say, 5 seconds. Is it achievable?

For example:

class FooTest < Minitest::Test
  def test_calculates_something
    # Something potentially too slow
  end
end

Upvotes: 0

Views: 476

Answers (1)

berkes
berkes

Reputation: 27603

You can use the Minitest PLugin loader to load a plugin. This is, by far, the cleanest solution. The plugin system is not very well documented, though.

Luckily, Adam Sanderson wrote an article on the plugin system.

The best news is that this article explains the plugin system by writing a sample plugin that reports slow tests. Try out minitest-snail, it is probably almost what you want.

With a little modification we can use the Reporter to mark a test as failed if it is too slow, like so (untested):

File minitest/snail_reporter.rb:

module Minitest
  class SnailReporter < Reporter
    attr_reader :max_duration

    def self.options
      @default_options ||= {
        :max_duration => 2
      }
    end

    def self.enable!(options = {})
      @enabled = true
      self.options.merge!(options)
    end

    def self.enabled?
      @enabled ||= false
    end

    def initialize(io = STDOUT, options = self.class.options)
      super
      @max_duration = options.fetch(:max_duration)
    end

    def record result
      @passed = result.time < max_duration
      slow_tests << result if !@passed
    end

    def passed?
      @passed
    end

    def report
      return if slow_tests.empty?

      slow_tests.sort_by!{|r| -r.time}

      io.puts
      io.puts "#{slow_tests.length} slow tests."
      slow_tests.each_with_index do |result, i|
        io.puts "%3d) %s: %.2f s" % [i+1, result.location, result.time]
      end
    end
  end
end

File minitest/snail_plugin.rb:

require_relative './snail_reporter'

module Minitest
  def self.plugin_snail_options(opts, options)
    opts.on "--max-duration TIME", "Report tests that take longer than TIME seconds." do |max_duration|
      SnailReporter.enable! :max_duration => max_duration.to_f
    end
  end

  def self.plugin_snail_init(options)
    if SnailReporter.enabled?
      io = options[:io]
      Minitest.reporter.reporters << SnailReporter.new(io)
    end
  end

end

Upvotes: 2

Related Questions