SkillAgentSearch skills...

Riot

Riot is a fast, expressive, and contextual ruby unit testing framework

Install / Use

/learn @thumblemonks/Riot
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Riot

A fast, expressive, and contextual ruby unit testing framework. Protest the slow test.

Installation

gem install riot

Usage

Build Status

In contrast to other popular Ruby testing frameworks such as Test::Unit, Shoulda and RSpec, Riot does not run a setup and teardown sequence before and after each test. This speeds up test execution quite a bit, but also changes how you write your tests. In general and in my opinion, you should avoid mutating any objects under test and if you use Riot you're pretty much going to have to.

In Riot, tests reside in contexts. Within these, a topic object is defined through a setup block. The actual assertions are then made with an asserts or denies block.

context "An empty Array" do
  setup { Array.new }
  asserts("it is empty") { topic.empty? }
  denies("it has any elements") { topic.any? }
end # An Array

As you can see, the setup block doesn't use any instance variables to save the object under test — rather, the return value of the block is used as the topic. This object can then be accessed in the assertions using the topic attribute. Furthermore, at their very basic level, assertions need only return a boolean. When using asserts, true indicates a pass while false indicates a fail; subsequently, when using denies, true indicates a failure whereas false indicates success.

Of course, you can nest contexts as well; the setup blocks are executed outside-in; as in, the parents' setups are run before the current context allowing for a setup hierarchy. teardown blocks are run inside out; the current context's teardowns are run before any of its parents'. This is what you would expect from other frameworks as well.

context "An Array" do
  setup { Array.new }

  asserts("is empty") { topic.empty? }

  context "with one element" do
    setup { topic << "foo" }
    asserts("array is not empty") { !topic.empty? }
    asserts("returns the element on #first") { topic.first == "foo" }
  end
end # An Array

By the way, you can put any kind of ruby object in your context description. Riot will call to_s on the actual value before it is used in a reporting context. This fact will become useful later ;)

Assertions

Well, how useful would Riot be if you could only return true/false from an assertion? Pretty useful, actually; but, we can make it more useful! No; that's not crazy. No it isn't. Yes; I'm sure.

We can do this with assertion macros. You can think of these as special assertion modifiers that check the return value of the assertion block. Actually, it's not that you can think of them this way; you should think of them this way.

Let's take this little for instance:

context "Yummy things" do
  setup { ["cookies", "donuts"] }

  asserts("#first") { topic.first }.equals("cookies")
end # Yummy things

First, how's that for a readable test? Second, you should notice that the assertion block will return the first item from the topic (which is assumed to be Enumerable in this case); if it isn't Enumerable, then you have other problems. Since the first element in the array is "cookies", the assertion will pass. Yay!

But wait, there's more. Riot is about helping you write faster and more readable tests. Notice any duplication in the example above (besides the value "cookies")? I do. How about that first notation in the assertion name and reference in the assertion block. Riot provides a shortcut which allows you to reference methods on the topic through the assertion name. Here's another way to write the same test:

context "Yummy things" do
  setup { ["cookies", "donuts"] }

  asserts(:first).equals("cookies")
end # Yummy things

Now that's real yummy. Want some more? Perhaps you just want to test the topic itself — not a method or attribute of it. You could do this:

context "Yummy things" do
  setup { ["cookies", "donuts"] }

  asserts("topic") { topic }.size(2)
end # Yummy things

But, as you can probably already guess, that's gross and redundant. To solve this, Riot provides the asserts_topic shortcut which is a helper that pretty much just does asserts("topic") { topic } for you.

context "Yummy things" do
  setup { ["cookies", "donuts"] }

  asserts_topic.size(2)
end # Yummy things

Yep, more readable.

Negative Assertions

Way back in the first code example we saw a reference to denies; this is what is called the negative assertion. You could probably also call it a counter assertion, but I don't. You can use denies with any assertion macro that you can use asserts with; it's just that denies expects the assertion to fail for the test to pass. For instance:

context "My wallet" do
  setup do
    Wallet.new(1000) # That's 1000 cents, or $10USD yo
  end

  asserts(:enough_for_lunch?)
  denies(:enough_for_lunch?)
end # My wallet

One of those will pass and the other will fail. If $10 is not enough for lunch the denies statement will pass; and then you should move to Chicago where it is enough (if only barely).

Built-in Assertion Macros

There are a bunch of built-in assertion macros for your everyday use. Be sure to write your own if these don't satisfy your every need. You will notice the two varying mechanisms for passing arguments into the macros: one is the conventional form of message passing (via actual arguments) and the other is derived from a provided block. If the macro expects one argument, you can use either form (but not both). If the macro accepts multiple arguments, the last argument you want to pass in can be provided via the block.

The advantage of using the block is that its innards are evaluated against the same scope that the assertion was evaluated against. This means you can use the same helpers and instance variables in the macro block to generate an expected value (if you so desire). It's also useful if you have a fairly complex routine for generating the expected value.

Equals

Compares equality of the actual value to the expected value using the == operator

asserts.equals(Object)
denies.equals(Object)
asserts.equals { Object }
denies.equals { Object }

Equivalent To

Compares equivalence of actual value to the expected value using the === operator

asserts.equivalent_to(Object)
denies.equivalent_to(Object)
asserts.equivalent_to { Object }
denies.equivalent_to { Object }

Assigns

Checks that the actual value has an instance variable defined within it's scope. You can also validate the value of that variable. Very much mimicing the assigns found in Rails-ish tests from way back in form, function, and need.

asserts("a person") { Person.new }.assigns(:email)
denies("a person") { Person.new }.assigns(:email)
asserts("a person") { Person.new(:email => "a@b.com") }.assigns(:email, "a@b.com")
denies("a person") { Person.new(:email => "a@b.com") }.assigns(:email, "a@b.com")
asserts.assigns { :email }
denies.assigns { :email }
asserts.assigns(:email) { "a@b.com" }
denies.assigns(:email) { "a@b.com" }

Nil

Simply checks the actual value for its nil-ness. Expects no arguments.

asserts.nil
denies.nil

Matches

Compares the actual value to a provided regular expression

asserts.matches(%r{Regex})
denies.matches(%r{Regex})
asserts.matches { /Regex/ }
denies.matches { /Regex/ }

Raises

Validates the type of exception raised from the assertion block. Optionally, you can give it the message you expected in the form of a literal string or even a portion of it.

asserts.raises(ExceptionClass)
denies.raises(ExceptionClass)
asserts.raises(ExceptionClass, "Expected message")
denies.raises(ExceptionClass, "Expected message")
asserts.raises(ExceptionClass) { "ted mess" }
denies.raises(ExceptionClass) { "ted mess" }

Kind Of

Validates the type of object returned from the assertion block

asserts.kind_of(Class)
denies.kind_of(Class)
asserts.kind_of { Class }
denies.kind_of { Class }

Responds To

Checks that the actual object respond_to? to a particular message

asserts.respond_to(:foo)
denies.respond_to(:foo)
asserts.respond_to { "foo" }
denies.respond_to { "foo" }
asserts.responds_to("foo")
denies.responds_to("foo")
asserts.responds_to { :foo }
denies.responds_to { :foo }

Includes

Checks for the existence of: a character or sequence of characters in a string, an element in an array, or a key in a hash.

asserts("this string") { "barbie q" }.includes("foo")
denies("this string") { "barbie q" }.includes("foo")
asserts("this array") { [1,2,3] }.includes(2)
denies("this array") { [1,2,3] }.includes(2)
asserts("this hash") { {:key1 => "foo"} }.includes(:key2)
denies("this hash") { {:key1 => "foo"} }.includes(:key2)
asserts.includes { "foo" }
denies.includes { "foo" }
asserts.includes { 2 }
denies.includes { 2 }
asserts.includes { :key }
denies.includes { :key }

Size

Compares the size of the actual object to the number you provide. Works with anything that responds to size(Numeric) (strings, arrays, hashes, etc).

asserts.size(Numeric)
denies.size(Numeric)
asserts.size { Numeric }
denies.size { Numeric }

Empty

Checks the result of calling empty? on the actual value. Expects no arguments.

asserts.empty
denies.empty

Same Elements

Compares actual to expected to see if they contain the same elements. Uses Set under-the-hood, just so you know.

asserts.same_elements(Array)
denies.same_elements(Array)
asserts.same_elements { Array }
denies.same_elements

Related Skills

View on GitHub
GitHub Stars315
CategoryDevelopment
Updated24d ago
Forks26

Languages

Ruby

Security Score

95/100

Audited on Mar 10, 2026

No findings