Skip to content

Instantly share code, notes, and snippets.

@seven1m
Last active August 29, 2015 14:01
Show Gist options
  • Save seven1m/e375bcdf2864da0022f1 to your computer and use it in GitHub Desktop.
Save seven1m/e375bcdf2864da0022f1 to your computer and use it in GitHub Desktop.
Really dumb script for converting from MiniTest::Unit to RSpec. This will get 80% of the grunt work done for you.
#!/usr/bin/env ruby
# This is a crude script to convert MiniTest::Unit files to RSpec files.
# To use:
#
# cd path/to/your/railsapp
# gem install parser unparser
# ruby to_rspec.rb
# rspec spec --fail-fast
#
# Now examine the error and make changes *to this script*.
# You can use `custom_fixes` and `custom_fixes_before` to make manual changes.
# (If you were to change the spec files by hand,
# they'll just get overwritten next time you run this script).
#
# Further, if you do get a spec file the way you want it, you can add it to
# the `IGNORED` list below.
require 'parser/current'
require 'unparser'
require 'fileutils'
class ToRspec
IGNORED = [
# paths here you don't want to overwrite, e.g.:
#'spec/models/verification_spec.rb'
]
def go!
Dir['test/**/*_test.rb'].each do |path|
puts path
body = File.read(path)
if path =~ /test\/unit\/helpers/
new_path = path.gsub('test/unit/helpers', 'spec/helpers')
elsif path =~ /test\/unit/
new_path = path.gsub('test/unit', 'spec/models')
elsif path =~ /test\/functional/
new_path = path.gsub('test/functional', 'spec/controllers')
elsif path =~ /test\/integration/
new_path = path.gsub('test/integration', 'spec/requests')
elsif path =~ /test\/fixtures/
new_path = path.gsub('test/fixtures', 'spec/fixtures')
elsif path =~ /test\/factories/
new_path = path.gsub('test/factories', 'spec/factories')
else
puts "don't know how change #{path}"
end
new_path.gsub!(/_test\.rb$/, '_spec.rb')
next if IGNORED.include?(new_path)
custom_fixes_before!(body, new_path)
spec_helper!(body, new_path)
describe!(body)
before!(body)
context_to_describe!(body)
should_to_it!(body)
test_to_it!(body)
assert_question!(body)
assert_cannot!(body)
assert_can!(body)
assert_equal!(body)
assert_include!(body)
assert_match!(body)
assert_response!(body)
assert_redirected_to!(body)
assert_nil!(body)
assert_not!(body)
assert!(body)
custom_fixes!(body, new_path)
FileUtils.mkdir_p(File.split(new_path).first)
File.open(new_path, 'w') { |f| f.write(body) }
puts
end
end
def spec_helper!(body, path)
replace_line!(body) do |line|
if line =~ /test_helper/
levels = path.split('spec/').last.count('/')
"require_relative '#{'../' * levels}spec_helper'"
end
end
end
def describe!(body)
replace_line!(body) do |line|
if line =~ /class ([:\w]+)Test < .*::(TestCase|IntegrationTest)\s*$/
"describe #{$1} do"
end
end
end
def before!(body)
replace_line!(body) do |line|
if line =~ /(\s*)(setup do|def setup)/
"#{$1}before do"
end
end
end
def context_to_describe!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)context (['"])(\w+)(['"]) do\s*$/
"#{$1}describe #{$2}#{$3}#{$4} do"
end
end
end
def should_to_it!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)should (['"])(.*)(['"]) do\s*$/
"#{$1}it #{$2}should #{$3}#{$4} do"
end
end
end
def test_to_it!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)test (['"])(.*)(['"]) do\s*$/
"#{$1}it #{$2}#{$3}#{$4} do"
end
end
end
def assert_question!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)assert (\!|not )(.*)\.(\w+)\?\s*$/
"#{$1}expect(#{$3}).to_not be_#{$4}"
elsif line =~ /^(\s*)assert (.*)\.(\w+)\?\s*$/
"#{$1}expect(#{$2}).to be_#{$3}"
end
end
end
def assert_equal!(body)
body.gsub!(/assert_equal(.*),\s*\n(.*)$/, "assert_equal\\1, \\2")
replace_line!(body) do |line|
if line =~ /^(\s*)(assert_equal.*)$/
(arg1, arg2) = get_args(line)
"#{$1}expect(#{arg2}).to eq(#{arg1})"
end
end
end
def assert_cannot!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)(assert_cannot.*)$/
(arg1, arg2, arg3) = get_args(line)
"#{$1}expect(#{arg1}).to_not be_able_to(#{arg2}, #{arg3})"
end
end
end
def assert_can!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)(assert_can.*)$/
(arg1, arg2, arg3) = get_args(line)
"#{$1}expect(#{arg1}).to be_able_to(#{arg2}, #{arg3})"
end
end
end
def assert_include!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)assert (!|not )?(.*)\.include\?\((.*)\)$/
negate = $2 ? '_not' : ''
"#{$1}expect(#{$3}).to#{negate} include(#{$4})"
end
end
end
def assert_match!(body)
body.gsub!(/assert_match(.*),\s*\n(.*)$/, "assert_match\\1, \\2")
replace_line!(body) do |line|
if line =~ /^(\s*)(assert_match.*)$/
(arg1, arg2) = get_args(line)
"#{$1}expect(#{arg2}).to match(#{arg1})"
end
end
end
# this works for some response codes (success, redirect, error), but not others (unauthorized)
# for the latter, you can change to this:
#
# expect(response.status) to eq(401)
#
# ... or build a custom matcher, e.g.
# https://github.com/churchio/onebody/blob/390f7cbd4248c4b1678a941b1eab0770d3cc519a/spec/support/response_matchers.rb
def assert_response!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)assert_response :(.*)$/
"#{$1}expect(response).to be_#{$2}"
end
end
end
def assert_redirected_to!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)assert_redirected_to (.*)$/
"#{$1}expect(response).to redirect_to(#{$2})"
end
end
end
def assert_nil!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)assert_nil (.*)$/
"#{$1}expect(#{$2}).to be_nil"
end
end
end
def assert_not!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)assert (!|not )(.*)\s*$/
"#{$1}expect(#{$3}).not_to be"
end
end
end
def assert!(body)
replace_line!(body) do |line|
if line =~ /^(\s*)assert (.*)\s*$/
"#{$1}expect(#{$2}).to be"
end
end
end
# custom fixes to be run _before_ all the transformations
def custom_fixes_before!(body, path)
# body.gsub!(/, 'StreamItem is (not |)shared\.'/, "")
end
# custom fixes to be run _after_ all the transformations
def custom_fixes!(body, path)
# body.gsub!(/describe MailMessage/, 'describe Mail::Message')
# comment_lines!(body, 7..56) if path =~ /person_spec\.rb/
end
def comment_lines!(body, nums)
lines = body.split(/\n/)
lines.each_with_index do |line, index|
if nums.include?(index+1)
lines[index] = '#' + line
end
end
lines.insert(nums.to_a.first-1, '# FIXME after rspec conversion')
body.replace(lines.join("\n"))
end
def replace_line!(body)
lines = body.split(/\n/)
lines.each_with_index do |line, index|
begin
new_line = yield(line, index)
rescue
puts '----------------------------------------'
puts "line number #{index+1}:"
puts line.strip
puts '----------------------------------------'
raise
end
lines[index] = new_line if new_line
end
body.replace(lines.join("\n"))
end
def get_args(line)
ast = Parser::CurrentRuby.parse(line)
args = ast.to_a[2..-1]
args.map { |a| Unparser.unparse(a) }
end
end
if $0 == __FILE__
ToRspec.new.go!
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment