How to organize minitest/unit tests? - ruby-on-rails

After using RSpec for several projects, I'm giving minitest/unit a go. I'm liking it so far, but I miss using describe/context blocks to group my tests/specs in a logical way.
I know minitest/spec provides this functionality, but I like that minitest/unit feels a bit closer to barebones Ruby.
Are there any gems that provide describe/context support for minitest/unit? Or, should I just live with my long, unorganized test files in minitest/unit?

I know several folks coming from RSpec to minitest struggling with the same question. They love the ability to nest using describe/context blocks and want to continue in minitest. There are several solutions:
Use minitest's spec DSL: While there are minor differences, the spec DSL gives you most (all?) of the good parts of the rspec DSL. The big difference is the lack of context blocks. But you can just as easily use describe in its place and everything works as you'd expect.
Use directories and files: I prefer this option. I dislike scrolling through a 300 line test file, regardless whether its using the spec DSL or the classical xUnit style. I do not find nesting unrelated tests helpful. The same rules for comprehension for code applies to tests. So break it up. Create a directory and place several files within it.
Here is an example of how my test files are organized:
test/
models/
user/
authentication_test.rb
email_test.rb
reservation_test.rb
user_test.rb
username_test.rb
I use this structure whether I'm using the spec DSL or the xUnit style. When using the spec DSL I specify what I'm testing in my describe block like so:
require "minitest_helper"
describe User, :authentications do
before do
# ...

You can also throw multiple classes into one test file:
module PizzaTest
class Isolation < ActiveSupport::TestCase
test "is awesome by default" do
assert Pizza.new.awesome?
end
end
class Integration < ActiveSupport::TestCase
fixtures :all
test "is awesome too" do
pizzas('one-with-everything').awesome?
end
end
end
and even nest test classes:
class PizzaTest < ActiveSupport::TestCase
test "is awesome by default" do
assert Pizza.new.awesome?
end
class Integration < ActiveSupport::TestCase
fixtures :all
test "is awesome too" do
assert pizzas('one-with-everything').awesome?
end
end
end

I prefer this way (only a little bit) but I think it easier to follow:
class ConventionalNameTest < ActiveSupport::TestCase
class ContextTest < ConventionalNameTest
# so much stuff...
end
class AnotherContextTest < ConventionalNameTest
# and some more...
end

Related

How to create cucumber (capybara?) helpers

I a have some code I'd like to refactor out of my step definitions and put them inside.. helpers?
Oh and please also say how to include them, I am really having a hard time finding any solid info on that.
Straight from the rspec documentation here: https://www.relishapp.com/rspec/rspec-core/docs/helper-methods/define-helper-methods-in-a-module#include-a-module-in-all-example-groups
Include a module in all example groups
Given a file named "include_module_spec.rb" with:
require './helpers'
RSpec.configure do |c|
c.include Helpers
end
RSpec.describe "an example group" do
it "has access to the helper methods defined in the module" do
expect(help).to be(:available)
end
end
When
I run rspec include_module_spec.rb
Then
the examples should all pass
You may also benefit from a a support/helpers folder Or equivalent which is covered pretty well here: How to include Rails Helpers on RSpec

How can I speed up Rails unit tests involving image upload/resizing?

My app does a lot with images. I use paperclip to attach them to models. I have tons of tests (Test::Unit) that involve creating images, these run pretty slowly.
I use FactoryGirl to create models in my tests. This is how I create image attachments:
factory :product_image_100_100 do
image File.new(File.join(::Rails.root.to_s, "/test/fixtures/images", "100_100.jpg"))
end
How can I fake the image upload or otherwise speed things up?
This snippet worked for me:
require 'test_helper'
class PhotoTest < ActiveSupport::TestCase
setup do
Paperclip::Attachment.any_instance.stubs(:post_process).returns(true)
end
# tests...
end
Upd. My current preference is to stub out ImageMagic globally, by adding the following to my test_helper.rb:
module Paperclip
def self.run(cmd, *)
case cmd
when "identify"
return "100x100"
when "convert"
return
else
super
end
end
end
(Adapted from here – btw, you may want to take a look at this article if you're interested in speeding up your tests)

Dynamic namespace rakes and parser classes with rails?

I have a collection of parsers that differ significantly in logic, but have the exact same methods and outputs.
I have devised somewhat of a master Rake and I am curious if what I have come up with could lead to some unexpected or weird behavior.
Essentially my parse.rake looks something like:
require 'app/models/provider'
require 'config/environment.rb'
Provider.all.each do |provider|
require "lib/tasks/#{provider.name}.rb"
Kernel.const_get(provider.name).provider = provider
namespace provider.name do
task :provider_task do #Parse method
Kernel.const_get(provider.name).provider_task()
end
end
end
Since classes are constants in ruby, Kernel.const_get is what I used to access class methods from a varname. My classes look like (ABC.rb):
Class ABC
cattr_accessor :provider
def self.provider_task()
#Parse and add stuff to environment
end
end
With this setup, I can easily invoke rake ABC:provider_task to run a specific parser. Also the cattr_accessor allows me to easily refer to the actual provider model. Thoughts?
I have used this with no issues for a few weeks. After reviewing with some colleagues, I found a few who previously used a similar approach with no issues. Only potential danger is naming conflicts with your ruby classes.

Rails: How do I write tests for a ruby module?

I would like to know how to write unit tests for a module that is mixed into a couple of classes but don't quite know how to go about it:
Do I test the instance methods by writing tests in one of the test files for a class that includes them (doesn't seem right) or can you somehow keep the tests for the included methods in a separate file specific to the module?
The same question applies to the class methods.
Should I have a separate test file for each of the classes in the module like normal rails models do, or do they live in the general module test file, if that exists?
IMHO, you should be doing functional test coverage that will cover all uses of the module, and then test it in isolation in a unit test:
setup do
#object = Object.new
#object.extend(Greeter)
end
should "greet person" do
#object.stubs(:format).returns("Hello {{NAME}}")
assert_equal "Hello World", #object.greet("World")
end
should "greet person in pirate" do
#object.stubs(:format).returns("Avast {{NAME}} lad!")
assert_equal "Avast Jim lad!", #object.greet("Jim")
end
If your unit tests are good, you should be able to just smoke test the functionality in the modules it is mixed into.
Or…
Write a test helper, that asserts the correct behaviour, then use that against each class it's mixed in. Usage would be as follows:
setup do
#object = FooClass.new
end
should_act_as_greeter
If your unit tests are good, this can be a simple smoke test of the expected behavior, checking the right delegates are called etc.
Use inline classes (I am not doing any fancy flexmock or stubba/mocha usage just to show the point)
def test_should_callout_to_foo
m = Class.new do
include ModuleUnderTest
def foo
3
end
end.new
assert_equal 6, m.foo_multiplied_by_two
end
Any mocking/stubbing library out there should give you a cleaner way to do this. Also you can use structs:
instance = Struct.new(:foo).new
class<<instance
include ModuleUnderTest
end
instance.foo = 4
If I have a module that is being used in many places I have a unit test for it which does just that (slide a test object under the module methods and test if the module methods function properly on that object).
What I like to do is create a new host class and mix the module into it, something like this:
describe MyModule do
let(:host_class) { Class.new { include MyModule } }
let(:instance) { host_class.new }
describe '#instance_method' do
it 'does something' do
expect(instance.instance_method).to do_something
end
end
end
I try to keep my tests focused only on the contract for that particular class/module. If I've proven the module's behavior in a test class for that module (usually by including that module in a test class declared in the spec for that module) then I won't duplicate that test for a production class that uses that module. But if there's additional behavior that I want to test for the production class, or integration concerns, I'll write tests for the production class.
For instance I have a module called AttributeValidator that performs lightweight validations kind of similar to ActiveRecord. I write tests for the module's behavior in the module spec:
before(:each) do
#attribute_validator = TestAttributeValidator.new
end
describe "after set callbacks" do
it "should be invoked when an attribute is set" do
def #attribute_validator.after_set_attribute_one; end
#attribute_validator.should_receive(:after_set_attribute_one).once
#attribute_validator.attribute_one = "asdf"
end
end
class TestAttributeValidator
include AttributeValidator
validating_str_accessor [:attribute_one, /\d{2,5}/]
end
Now in a production class that includes the module, I won't re-assert that the callbacks are made, but I may assert that the included class has a certain validation set with a certain regular expression, something particular to that class, but not reproducing the tests I wrote for the module. In the spec for the production class, I want to guarantee that particular validations are set, but not that validations work in general. This is a kind of integration test, but one that doesn't repeat the same assertions I made for the module:
describe "ProductionClass validation" do
it "should return true if the attribute is valid" do
#production_class.attribute = #valid_attribute
#production_class.is_valid?.should be_true
end
it "should return false if the attribute is invalid" do
#production_class.attribute = #invalid_attribute
#production_class.is valid?.should be_false
end
end
There is some duplication here (as most integration tests will have), but the tests prove two different things to me. One set of tests prove the general behavior of the module, the other proves particular implementation concerns of a production class that uses that module. From these tests I know that the module will validate attributes and perform callbacks, and I know that my production class has a specific set of validations for specific criteria unique to the production class.
Hope that helps.
In minitest since each test is explicitly a class you can just include the module to the test and test the methods:
class MyModuleTest < Minitest::Test
include MyModule
def my_module_method_test
# Assert my method works
end
end
I would generally test the module in as much isolation as possible, essentially testing the methods, with just enough code, mocks and stubs to get it working.
I would then probably also have tests for the classes the modules is included in. I may not test every class, but would test enough of the classes to get good coverage and have insight into any issues that arise. These tests don't need to explicitly test the module, but would certainly test it's usage in particular scenarios.
Each set of tests would have its own file.

Adding rspec test for library module doesn't seem to pickup Expectations and Matchers

I'm adding more rspec testing to my app and would like to test a ScoringMethods module, which is in /lib/scoring_methods.rb. So I added a /spec/lib directory and added scoring_methods_spec.rb there. I required spec_helper and set up the describe block as so:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe ScoringMethods do
describe "should have scorePublicContest method" do
methods = ScoringMethods.instance_methods
methods[0].should match(/scorePublicContest/)
end
end
Now methods[0] is a String and there is no problem matching the public method name with the regular expression. And the relative path to "spec_helper" is correct.
The problem is that the entire setup doesn't seem to use the rspec library.
Running the example yields:
./spec/lib/scoring_methods_spec.rb:7: undefined method `match' for Spec::Rails::Example::RailsExampleGroup::Subclass_1::Subclass_1:Class (NoMethodError)
...
The entire Expectation and Matcher support seems to be missing. To test my supposition, I changed a working helper spec by replacing "is_instance_of" to "is_foobar_of". That test simply fails and says "is_foobar_of" is not a method of the targeted object; that it, this entire Spec::Rails::Example... hierarchy isn't present.
I've tried using other matchers as well. I've tried "be_instance_of" and some others. It seems that I'm not including the rspec library properly.
Finally, ScoringMethods is a module, just the same way Helpers are modules. So I thought that it would be possible to test a module (as opposed to classes, such as Controllers and Models).
I'd greatly appreciate your thoughts on what I've done wrong. Perhaps there is a more effective way of testing library modules? Thanks!
You should include your test block in an "it" block. For example:
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
describe ScoringMethods do
describe "should have scorePublicContest method" do
it "should have a scorePublicContest method" do
methods = ScoringMethods.instance_methods
methods[0].should match(/scorePublicContest/)
end
end
end
You will find that the methods names returned aren't guaranteed to be in the order they exist in the file.
A model we often use when testing Modules is to include the module in either a class created for the test (inside the spec file) or included inside the spec itself.

Resources