I have a unit test in Rails, the model it is testing uses Sequel for our own internal reasons, but the test data is created using factory_girl which seems to wrap the whole test in an activerecord transaction. I can use self.use_transactional_fixtures = false but was wondering if there was a way to not leave the test datain my db after my test is done.
I'm not sure what test library you are using. For RSpec 1, the following runs each spec example in its own transaction:
class Spec::Example::ExampleGroup
def execute(*args, &block)
x = nil
Sequel::Model.db.transaction{x = super(*args, &block); raise Sequel::Error::Rollback}
x
end
end
For RSpec 2, you can use an around filter to accomplish the same thing. I'm not sure how to do it in test/unit, you probably need to override Test::Unit::TestCase#run.
Related
I have been looking around the internet for a long, frustrating, while, and I'm still quite confused as to what the purpose of the teardown() method is in MiniTest and how it should be used.
The basic gist I have is that it is 1-run after each test, and 2-undoes things that were done during the test in order to allow future tests to run in a clean environment.
However, I am unclear on the nature of things it needs to undo: Does it need to empty the DB? Reset class variables? etc.
I am also unclear on whether the method is supposed to be explicitly filled out or not. I have found many examples where teardown() is completely left out of the example.
(My best guess is that there is a super-method teardown that runs automatically and takes care of certain things. That would explain why it is often left out, and would also explain why some things are reset in a given teardown() method and some aren't. But I still don't know which things are and which aren't.)
In short:
Does teardown need to be explicitly created? In what circumstances would it need to be overwritten and in which wouldn't it be?
The simplest answer is that you use #teardown in every test but you don't need to worry about it. Similar to the Rails lifecycle, there is a Minitest lifecycle. There are hooks to inject logic and behavior to be used by your tests. The main one in Rails tests is the database transaction. Each test that uses ActiveSupport::TestCase runs in a database transaction. The flow is like this:
Create the database transaction (Minitest::Test#setup)
Run your test method (MyTest#test_something)
Rollback the database transaction (Minitest::Test#teardown)
It is somewhat common for folks to use #setup to create objects for use in tests. After the test method completes the test object is garbage collected, so most folks don't use #teardown to clean up after the test. Because of this #teardown is typically a more advanced feature that you don't normally use when writing tests. I see it used much more often in testing libraries that enhance Minitest.
But there are times I do use #teardown in my tests. Here is an example of when I might use it.
require "minitest/autorun"
class Foo
def initialize namer
#namer = namer
end
def name
#namer.name
end
end
class FooTest < Minitest::Test
def setup
#namer_mock = Minitest::Mock.new
#namer_mock.expect :name, "foo"
#foo = Foo.new #namer_mock
end
def test_name
assert_equal "foo", #foo.name
end
def teardown
#namer_mock.verify
end
end
I am a big fan of FactoryGirl, but have never understood the ".build_stubbed" method and would like to incorporate stubbing/mocking into my TDD. Can someone help me with the basics of what kind of stubbing I can do with FactoryGirl? Should I start using Mocha or another similar GEM to handle the mocking/stubbing or is any of this included in the Rails 4 default MiniTest?
The factory girl command build_stubbed means that the object is created (and also all its associated objects) but no objects are inserted into the database. You should use this if you want faster tests and do not need to have the objects in the database.
This means that the command does not have to do much with stubbing or mocking.
I myself have only experience with Mocha and can say that it very easy to use it for stubbing and mocking.
For stubbing out a command (e.g. of the object Person)
person = Person.new
person.stubs(:name).returns('Robert')
The obove example creates an instance of the person and stubbes out the method name to always return 'Robert'.
For mocking out the same command
person = Person.new
person.expects(:name).returns('Robert')
The above does the same as stubbing out the method. With the only difference that now the test fails if the method name is not called exactly once.
I'm trying to write a test for ActiveRecord - and Rails uses MiniTest for its tests, so I don't have a choice of test framework. The condition I want to test is this (from the db:create rake tasks, pulled into a method for the purpose of this example):
def create_db
if File.exist?(config['database'])
$stderr.puts "#{config['database']} already exists"
end
end
So, I want to test that $stderr receives puts if the File exists, but otherwise does not. In RSpec, I would have done this:
File.stub :exist? => true
$stderr.should_receive(:puts).with("my-db already exists")
create_db
What's the equivalent in MiniTest? assert_send doesn't seem to behave as I expect (and there's not really any documentation out there - should it go before the execution, like should_receive, or after?). I was thinking I could temporarily set $stderr with a mock for the duration of the test, but $stderr only accepts objects that respond to write. You can't stub methods on mocks, and I don't want to set an expectation of the write method on my stderr mock - that'd mean I'm testing an object I'm mocking.
I feel like I'm not using MiniTest the right way here, so some guidance would be appreciated.
An update: here is a solution that works, but it is setting the expectation for :write, which is Not Right.
def test_db_create_when_file_exists
error_io = MiniTest::Mock.new
error_io.expect(:write, true)
error_io.expect(:puts, nil, ["#{#database} already exists"])
File.stubs(:exist?).returns(true)
original_error_io, $stderr = $stderr, error_io
ActiveRecord::Tasks::DatabaseTasks.create #configuration
ensure
$stderr = original_error_io unless original_error_io.nil?
end
So, it turns out Rails uses Mocha in combination with Minitest, which means we can take advantage of Mocha's far nicer message expectations. A working test looks like this:
def test_db_create_when_file_exists
File.stubs(:exist?).returns(true)
$stderr.expects(:puts).with("#{#database} already exists")
ActiveRecord::Tasks::DatabaseTasks.create #configuration
end
I am trying to test a controller with RSpec but am having a problem because a function in the controller requires a database.
the line of code in the controller looks something like:
#myallresources = Myrsources.all
where Myresources just inherits from ActiveRecord::Base
however, because there is no database, there is nothing to load and #myallresources is just an empty array, causing the test to fail. Is there a way to connect to a database while running the rspec?
I am very new to RSpec and rails so any help would be very appreciated. Thanks.
You shouldn't use a database connection in your controller specs.
Check the section about database isolation on this page http://rspec.info/rails/writing/controllers.html
Basically you have to mock or stub your ActiveRecord models, as those should be tested separately in the models specs. Here's a simple example using mock_model:
before do
mocks = (1..3).map { mock_model(MyResource) }
MyResource.should_receive(:all).and_return(mocks)
end
Put this inside the same block where reside the describe definition testing for the actions that use MyResource.all.
You can find good explanation of mocks and stubs in following links:
http://relishapp.com/rspec/rspec-rails/v/2-5/docs/mocks/mock-model
http://relishapp.com/rspec/rspec-rails/v/2-5/docs/mocks/stub-model
When I started my Rails app, I chose RSpec as my testing framework, because of all the hype. Now, I'm trying to write more tests, and I find that I don't like to write tests this way. I'd like to switch back to the default testing framework.
How could I do this switch? I'm using Rails 3.0.5
Don't know if you are aware, but if you use assertions instead of rspec-expectations (obj.should matcher), which you can already do without any additional configuration, then the only difference is this:
# w/ Test::Unit
class FooControllerTest < ActionController::TestCase
test "something or other" do
...
end
end
# w/ RSpec
describe FooController do
it "does something or other" do
...
end
end
Everything else that you can write using Test::Unit in Rails, you can write using RSpec exactly the same way.
Then you get all the non-syntax-related benefits of RSpec like readable output, a robust command line tool with its own -help output, etc.
I realize that doesn't answer the question you're asking, but I hope it helps you in your decision process.
It's not an either/or situation. You can have RSpec and Test::Unit tests in the same Rails application, so just begin writing Test::Unit tests. No need to switch, just do new development with Test::Unit.
Depending on how you run your tests, your CI setup, and how you have modified your app this may be more or less changes, but in practice these are straightforward.
in config/application.rb
module AppName
class Application < Rails::Application
generate.test_framework :test_unit, :fixture => false # :rspec <- before
...
New models/controllers generate the basic test/* files