Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
reads settings from a .wrong file
improve README
can parse a here doc defined inside the block
better Ruby version printing in test_helper
  • Loading branch information
alexch committed Oct 6, 2010
1 parent 04747d4 commit dbaf06a
Show file tree
Hide file tree
Showing 12 changed files with 206 additions and 102 deletions.
2 changes: 2 additions & 0 deletions .wrong
@@ -0,0 +1,2 @@
test_setting = "xyzzy"
color = false # sadly, we must disable this when testing wrong
4 changes: 4 additions & 0 deletions HISTORY.txt
@@ -1,3 +1,7 @@
== 0.4.1

* reads settings from a .wrong file

== 0.4.0

* "require 'wrong'" and "include Wrong' now get everything; see README for details on how to get less than the whole enchilada
Expand Down
82 changes: 50 additions & 32 deletions README.markdown
Expand Up @@ -4,7 +4,7 @@

## Abstract ##

Wrong provides a general assert method that takes a predicate block. Assertion failure messages are rich in detail. The Wrong idea is to replace all those countless assert\_this, assert\_that, should\_something library methods which only exist to give a more useful failure message than "assertion failed". Wrong replaces all of them in one fell swoop, since if you can write it in Ruby, Wrong can make a sensible failure message out of it.
Wrong provides a general assert method that takes a predicate block. Assertion failure messages are rich in detail. The Wrong idea is to replace all those countless `assert\_this`, `assert\_that`, `should\_something` library methods which only exist to give a failure message that's not simply "assertion failed". Wrong replaces all of them in one fell swoop, since if you can write it in Ruby, Wrong can make a sensible failure message out of it.

We'd very much appreciate feedback and bug reports. There are plenty of things left to be done to make the results look uniformly clean and beautiful. We want your feedback, and especially to give us cases where either it blows up or the output is ugly or uninformative.

Expand All @@ -16,11 +16,7 @@ Inspired by [assert { 2.0 }](http://assert2.rubyforge.org/) but rewritten from s

gem install wrong

Under JRuby, the above may cause errors; if so, then try

gem install wrong-jruby

which untangles some dependencies.
We have deployed gems for both Ruby and JRuby; if you get dependency issues on your platform, please let us know what Ruby interpreter and version you're using and what errors you get, and we'll try to track it down.

## Usage ##

Expand Down Expand Up @@ -139,30 +135,33 @@ or this
? The Wrong way has the advantage of being plain, transparent Ruby code, not an awkward DSL that moves "equal" out of its natural place between the comparands. Plus, WYSIWYG! You know just from looking at it that "equal" means `==`, not `eql?` or `===` or `=~`.

Moreover, much like TDD itself, Wrong encourages you to write cleaner code. If your assertion messages are not clear and "Englishy", then maybe it's time for you to refactor a bit -- extract an informatively named variable or method, maybe push some function onto its natural object *a la* the [Law of Demeter](http://en.wikipedia.org/wiki/Law_of_Demeter)...
Also, try not to call any methods with side effects inside an assert. In addition to being bad form, this can cause messed-up failure messages, since the side effects may occur several times in the process of building the message.

Wrong also lets you put the expected and actual values in any order you want! Consider the failure messages for

assert { current_user == "joe" } # => Expected (current_user == "joe") but current_user is "fred"
assert { "joe" == current_user } # => Expected ("joe" == current_user) but current_user is "fred"

You get just the information you want, and none you don't want. At least, that's the plan! :-)
You get all the information you want, and none you don't want. At least, that's the plan! :-)

## Algorithm ##

So wait a second. How do we do it? Doesn't Ruby have [poor support for AST introspection](http://blog.zenspider.com/2009/04/parsetree-eol.html)? Well, yes, it does, so we cheat: we figure out what file and line the assert block is defined in, then open the file, read the code, and parse it directly using Ryan Davis' amazing [RubyParser](http://parsetree.rubyforge.org/ruby_parser/) and [Ruby2Ruby](http://seattlerb.rubyforge.org/ruby2ruby/). You can bask in the kludge by examining `chunk.rb` and `assert.rb`. If you find some code it can't parse, please send it our way.

Before you get your knickers in a twist about how this is totally unacceptable because it doesn't support this or that use case, here are our caveats and excuses:

* It works! Tested in 1.8.6, 1.8.7, 1.9.1, and 1.9.2-rc2. (Thank you, [rvm](http://rvm.beginrescueend.com/)!)
* It works! Tested in MRI 1.8.6, 1.8.7, 1.9.1, 1.9.2, and JRuby 1.5.3. (Thank you, [rvm](http://rvm.beginrescueend.com/)!)
* Your code needs to be in a file.
* If you're developing Ruby code without saving it to a mounted disk, then sorry, Wrong is not right for you.
* We monkey-patch IRB so if you do `irb -rwrong` it'll save off your session in a place Wrong can read it.
* We monkey-patch IRB so if you do `irb -rwrong` it'll save off your session in memory where Wrong can read it.
* It'd be nice if it could work inside a `-e` block but as far as we can tell, there's no way to grab that `-e` code from inside Ruby.
* It's a development-time testing library, not a production runtime library, so there are no security or filesystem issues.
* `eval` isn't evil, it's just misunderstood.
* It makes a few assumptions about the structure of your code, leading to some restrictions:
* You can't have more than one call to `assert` per line. (This should not be a problem since even if you're nesting asserts for some bizarre reason, we assume you know where your Return key is. And actually, technically you can put two asserts on a line, but it always describes the first one it sees, which means that if the second one executes, its failure message will be incorrect or broken.)
* You can't have more than one call to `assert` per line. (This should not be a problem since even if you're nesting asserts for some bizarre reason, we assume you know where your Return key is.)
* You can't use metaprogramming to write your assert blocks.
* All variables and methods must be available in the binding of the assertion block.
* All variables and methods must be available in the binding of the assert block.
* Passing a proc around and eventually calling assert on it might not work in some Ruby implementations.

## Adapters ##

Expand All @@ -172,9 +171,9 @@ Currently we support

* Test::Unit - `require 'wrong/adapters/test_unit'`
* Minitest - `require 'wrong/adapters/minitest'`
* RSpec - `require 'wrong/adapters/rspec'`
* RSpec - `require 'wrong/adapters/rspec'` (now supports both 1.3 and 2.0)

To use these, put the appropriate `require` in your helper; it should extend the framework enough that you can use `assert { }` in your test cases without extra fussing around.
To use these, put the appropriate `require` in your helper, **after** requiring your test framework; it should extend the framework enough that you can use `assert { }` in your test cases without extra fussing around.

## Explanations ##

Expand Down Expand Up @@ -248,34 +247,52 @@ To use these formatters, you have to explicitly `require` them! You may also nee

[Bug: turns out 'diff' and 'diff-lcs' are incompatible with each other. We're working on a fix.]

## Color ##
## Config ##

These settings can either be set at runtime on the `Wrong.config` singleton, or inside a `.wrong` file in the current directory or a parent. In the `.wrong` file just pretend every line is preceded with `Wrong.config.` -- e.g. if there's a setting called `ice_cream`, you can do any of these in your `.wrong` file

ice_cream # => Wrong.config[:ice_cream] => true
ice_cream = true # => Wrong.config[:ice_cream] => true
ice_cream = "vanilla" # => Wrong.config[:ice_cream] => "vanilla"

or any of these at runtime:

Wrong.config.ice_cream # => Wrong.config[:ice_cream] => true
Wrong.config.ice_cream = true # => Wrong.config[:ice_cream] => true
Wrong.config.ice_cream = "vanilla" # => Wrong.config[:ice_cream] => "vanilla"

### Color ###

Apparently, no test framework is successful unless and until it supports console colors. So now we do. Call

Wrong.config.color

Apparently, no test framework is successful unless and until it supports console colors. So now we do. Put
in your test helper or rakefile or wherever, or put

Wrong.config[:color] = true
color

in your test helper or rakefile or wherever and get ready to be **bedazzled**. If you need custom colors, let us know.
in your `.wrong` file and get ready to be **bedazzled**. If you need custom colors, let us know.

## Aliases ##
### Aliases ###

An end to the language wars! Name your "assert" and "deny" methods anything you want. Here are some suggestions:
An end to the language wars! Name your "assert" and "deny" methods anything you want. In your code, use `Wrong.config.alias_assert` and `Wrong.config.alias_deny`, and in your `.wrong` file, use Here are some suggestions:

Wrong.config.alias_assert(:expect)
Wrong.config.alias_assert(:should) # This looks nice with RSpec
Wrong.config.alias_assert(:confirm)
Wrong.config.alias_assert(:be)
alias_assert :expect
alias_assert :should # This looks nice in RSpec
alias_assert :confirm
alias_assert :be

Wrong.config.alias_assert(:is)
Wrong.config.alias_deny(:aint)
alias_assert :is
alias_deny :aint

Wrong.config.alias_assert(:assure)
Wrong.config.alias_deny(:refute)
alias_assert :assure
alias_deny :refute

Wrong.config.alias_assert(:yep)
Wrong.config.alias_deny(:nope)
alias_assert :yep
alias_deny :nope

Wrong.config.alias_assert(:yay!)
Wrong.config.alias_deny(:boo!)
alias_assert :yay!
alias_deny :boo!

Just don't use "`aver`" since we took that one for an internal method in `Wrong::Assert`.

Expand All @@ -294,7 +311,8 @@ If you're in Ruby 1.8, you **really** shouldn't do it! But if you do, you can us

## Etc ##

* Github projects: <http://github.com/alexch/wrong>, <http://github.com/sconover/wrong>
* Mailing list: <http://groups.google.com/group/wrong-rb>
* Github project: <http://github.com/sconover/wrong>
* Tracker project: <http://www.pivotaltracker.com/projects/109993>
* the [Wrong way translation table (from RSpec and Test::Unit)](https://spreadsheets.google.com/pub?key=0AouPn6oLrimWdE0tZDVOWnFGMzVPZy0tWHZwdnhFYkE&hl=en&output=html). (Ask <alexch@gmail.com> if you want editing privileges.)
* the [Wrong slides](http://www.slideshare.net/alexchaffee/wrong-5069976) that Alex presented at Carbon Five and GoGaRuCo
2 changes: 1 addition & 1 deletion examples.rb
Expand Up @@ -11,7 +11,7 @@

include Wrong

Wrong.config[:color] = true
Wrong.config.color # or just put the line "color" in a file called ".wrong" in the current dir

def failing
e = rescuing do
Expand Down
18 changes: 10 additions & 8 deletions lib/wrong/chunk.rb
Expand Up @@ -72,17 +72,19 @@ def build_sexp
end)
end

def read_source_file(file, dir = ".")
File.read "#{dir}/#{file}"
def read_source_file(file)
Chunk.read_here_or_higher(file)
end

def self.read_here_or_higher(file, dir = ".")
File.read "#{dir}/#{file}"
rescue Errno::ENOENT, Errno::EACCES => e
# we may be in a chdir underneath where the file is, so move up one level and try again
parent = "#{dir}/..".gsub(/^(\.\/)*/, '')
if File.expand_path(dir) == File.expand_path(parent)
raise Errno::ENOENT, "couldn't find #{file}"
end
read_source_file(file, parent)

read_here_or_higher(file, parent)
end

# Algorithm:
Expand Down Expand Up @@ -196,10 +198,10 @@ def details
raises = raises.bold.color(:red)
end
formatted_exeption = if e.message and e.message != e.class.to_s
indent(2, part, " ", raises, ": ", indent_all(3, e.message))
else
indent(2, part, " ", raises)
end
indent(2, part, " ", raises, ": ", indent_all(3, e.message))
else
indent(2, part, " ", raises)
end
details << formatted_exeption
end
end
Expand Down
31 changes: 30 additions & 1 deletion lib/wrong/config.rb
@@ -1,9 +1,38 @@
require "wrong/chunk"

module Wrong
def self.load_config
settings = begin
Chunk.read_here_or_higher(".wrong")
rescue Errno::ENOENT => e
# couldn't find it
end
Config.new settings
end

def self.config
@config ||= Config.new
@config ||= load_config
end

def self.config=(new_config)
@config = load_config
end

class Config < Hash
def initialize(string = nil)
if string
instance_eval string.gsub(/^(.*=)/, "self.\\1")
end
end

def method_missing(name, value = true)
name = name.to_s
if name =~ /=$/
name.gsub!(/=$/, '')
end
self[name.to_sym] = value
end

def alias_assert(method_name)
Wrong::Assert.send(:alias_method, method_name, :assert)
self.assert_method_names << method_name.to_sym unless self.assert_method_names.include?(method_name)
Expand Down
6 changes: 3 additions & 3 deletions lib/wrong/sexp_ext.rb
Expand Up @@ -12,8 +12,8 @@ def to_ruby

# visit every node in the tree, including the root, that is an Sexp
# todo: test
def each_subexp(&block)
yield self
def each_subexp(include_root = true, &block)
yield self if include_root
each do |child|
if child.is_a?(Sexp)
child.each_subexp(&block)
Expand Down Expand Up @@ -42,7 +42,7 @@ def assertion
private
def nested_assertions
assertions = []
self.each_of_type(:iter) { |sexp| assertions << sexp if sexp.assertion? }
self.each_subexp(false) { |sexp| assertions << sexp if sexp.assertion? }
assertions
end

Expand Down
2 changes: 1 addition & 1 deletion lib/wrong/version.rb
@@ -1,3 +1,3 @@
module Wrong
VERSION = "0.4.0" unless defined?(Wrong::VERSION)
VERSION = "0.4.1" unless defined?(Wrong::VERSION)
end
8 changes: 4 additions & 4 deletions test/assert_advanced_test.rb
Expand Up @@ -20,18 +20,18 @@ def assert_later(&p)
end
end

xit "can parse a here doc defined inside the block" do
it "can parse a here doc defined inside the block" do
# todo: test in Chunk too
assert { "123\n456" == <<-TEXT
assert { "123\n456\n" == <<-TEXT
123
456
TEXT
}
end

xit "can parse a here doc defined outside the block" do
it "can parse a here doc defined outside the block" do
# todo: test in Chunk too
assert { "123\n456" == <<-TEXT }
assert { "123\n456\n" == <<-TEXT }
123
456
TEXT
Expand Down

0 comments on commit dbaf06a

Please sign in to comment.