[ANN] NullDB 0.0.1

== What

NullDB is a Rails database connection adapter that interprets common
database operations as no-ops. It is the Null Object pattern as
applied to database adapters.

== How

Once installed, NullDB can be used much like any other ActiveRecord
database adapter:

ActiveRecord::Base.establish_connection :adapter => nulldb

NullDB needs to know where you keep your schema file in order to
reflect table metadata. By default it looks in
RAILS_ROOT/db/schema.rb. You can override that by setting the
+schema+ option:

ActiveRecord::Base.establish_connection :adapter => nulldb,
:schema => foo/myschema.rb

There is a helper method included for configuring RSpec sessions to
use NullDB. Just put the following in your spec/spec_helper.rb:

Spec::Runner.configure do |config|
::NullDB.insinuate_into_spec(config)
end

You can also experiment with putting NullDB in your database.yml:

unit_test:
adapter: nulldb

However, due to the way Rails hard-codes specific database adapters
into its standard Rake tasks, you may find that this generates
unexpected and difficult-to-debug behavior. Workarounds for this are
under development.

== Why

NullDB is intended to assist in writing fast database-independant unit
tests for ActiveRecord classes. For why you would want to test your
models without the database, see:
Rails: UnitRecord - Test without the Database - Dan Manges's Blog.

NullDB was inspired by the ARBS[http://arbs.rubyforge.org/] and
UnitRecord[http://unit-test-ar.rubyforge.org/] libraries. It differs
from them in a couple of ways:

  1. It works. At the time of writing both ARBS and UnitRecord were
    not working for me out of the box with Rails 2.0.

  2. It avoids monkey-patching as much as possible. Rather than
    re-wiring the secret inner workings of ActiveRecord (and thus being
    tightly coupled to those inner workings), NullDB implements the
    same [semi-]well-documented public interface that the other standard
    database adapters, like MySQL and SQLServer, implement.

  3. UnitRecord takes the approach of eliminating database interaction
    in tests by turning almost every database interaction into an
    exception. NullDB recognizes that ActiveRecord objects typically
    can’t take two steps without consulting the database, so instead it
    turns database interactions into no-ops.

One concrete advantage of this null-object pattern design is that it
is possible with NullDB to test +after_save+ hooks. With NullDB, you
can call +#save+ and all of the usual callbacks will be called - but
nothing will be saved.

== Limitations

  • It is not an in-memory database. Finds will not work. Neither
    will #reload, currently.
  • It has only the most rudimentery schema/migration support. Complex
    migrations will probably break it.
  • Lots of other things probably don’t work. Patches welcome!

== Who

NullDB was written by Avdi G. mailto:[email protected]

== Where

== Changes

  • Version 0.0.1 (2007-02-18)
    • Initial Release

Avdi,

This sounds like a cool idea. One question. Would the usage pattern
then be to use a mock/stub for the find methods and let the other save
methods fall though to the NullDB connector?

Ed

On Mon, Feb 18, 2008 at 12:23 AM, Avdi G.
[email protected] wrote:

database adapter:

adapter: nulldb

models without the database, see:
re-wiring the secret inner workings of ActiveRecord (and thus being
One concrete advantage of this null-object pattern design is that it

  • Lots of other things probably don’t work. Patches welcome!
    == Changes

  • Version 0.0.1 (2007-02-18)


Ed Howland

“The information transmitted is intended only for the person or entity
to which it is addressed and may contain proprietary, confidential
and/or legally privileged material. Any review, retransmission,
dissemination or other use of, or taking of any action in reliance
upon, this information by persons or entities other than the intended
recipient is prohibited. If you received this in error, please contact
the sender and delete the material from all computers.”

Ed Howland wrote:

Avdi,

This sounds like a cool idea. One question. Would the usage pattern
then be to use a mock/stub for the find methods and let the other save
methods fall though to the NullDB connector?

Yes, that’s accurate. Check the documentation for the latest revision,
too; there are some new RSpec helpers.


Avdi

Avdi G. wrote:

Ed Howland wrote:

Avdi,

This sounds like a cool idea. One question. Would the usage pattern
then be to use a mock/stub for the find methods and let the other save
methods fall though to the NullDB connector?

Yes, that’s accurate. Check the documentation for the latest revision,
too; there are some new RSpec helpers.


Avdi

Do you have any advice on how to setup NullDB in a JRuby environment?
I’m using JRuby 1.5.1, Test::Unit, Shoulda, and will be using
Factory_Girl for functional and integration tests. I’m fairly new to
unit testing so as much step by step as you can give would be great!

Cheers,
Eric

Avdi G. wrote:

== What

NullDB is a Rails database connection adapter that interprets common
database operations as no-ops. It is the Null Object pattern as
applied to database adapters.
[…]
== Why

NullDB is intended to assist in writing fast database-independant unit
tests for ActiveRecord classes. For why you would want to test your
models without the database, see:
Rails: UnitRecord - Test without the Database - Dan Manges's Blog.

I’m not at all convinced that this is a good way of testing (and in my
experience, ThoughtWorks’ software has lots of problems, which could say
something about their testing practices). However, assuming for the
moment that it is good…
[…]

  • It is not an in-memory database. Finds will not work. Neither
    will #reload, currently.

So how is this of any use at all? Normally, when I use tests with
database operations, I do so to confirm that I’m writing to the database
what I think I am. (Sure, I could use RSpec’s mocks to test that – and
then I wouldn’t need NullDB in the first place.) By removing the
capability to save records, it seems to me that you’ve removed any
utility that this might have had. Am I missing something? How do you
find this puppy useful?

Best,
–Â
Marnen Laibow-Koser
http://www.marnen.org
[email protected]

Sent from my iPhone

Marnen Laibow-Koser wrote:

Avdi G. wrote:

== What

NullDB is a Rails database connection adapter that interprets common
database operations as no-ops. It is the Null Object pattern as
applied to database adapters.
[…]
== Why

NullDB is intended to assist in writing fast database-independant unit
tests for ActiveRecord classes. For why you would want to test your
models without the database, see:
Rails: UnitRecord - Test without the Database - Dan Manges's Blog.

I’m not at all convinced that this is a good way of testing (and in my
experience, ThoughtWorks’ software has lots of problems, which could say
something about their testing practices). However, assuming for the
moment that it is good…
[…]

  • It is not an in-memory database. Finds will not work. Neither
    will #reload, currently.

So how is this of any use at all? Normally, when I use tests with
database operations, I do so to confirm that I’m writing to the database
what I think I am. (Sure, I could use RSpec’s mocks to test that – and
then I wouldn’t need NullDB in the first place.) By removing the
capability to save records, it seems to me that you’ve removed any
utility that this might have had. Am I missing something? How do you
find this puppy useful?

Best,
–Â
Marnen Laibow-Koser
http://www.marnen.org
[email protected]

Sent from my iPhone

Marnen,

I’m fairly new to unit testing and TDD, but I think I understand the
concept of what they’re talking about here. The point is that if you are
writing to the database during unit tests, you’re actually testing the
database adapter and abstraction layer, in this case: ActiveRecord, and
not the logic that you have written. ActiveRecord, has been tested
extensively, it would be redundant to test it again. By removing the
database from the testing, you can focus on testing your business
logic… and you have the side benefit of it being quicker, which is
essential for TDD to be a practical way to develop software. I think
what you’re describing is considered to be functional testing or
integration test, not unit testing. Functional and integration testing
is equally important, it just occurs at a different level.

This is one of the rare times when I think Ruby got it wrong when
compared with other languages, most other more mature languages Java,
C++, etc… provide tools to avoid hitting the database during unit
testing for just this reason. I think Rails, and Ruby in general is
heading in this direction too. The next generation of ORM databases like
DataMapper have this sort of thing built in. Hope this helps!

Cheers,
Eric

Eric S. wrote:

Marnen,

I’m fairly new to unit testing and TDD,

I’ve been doing test-first development as long as I’ve been doing Rails
development, nearly 3 years. (Whether that means that I too am new to
unit testing is left as an exercise for the student.)

? but I think I understand the

concept of what they’re talking about here. The point is that if you are
writing to the database during unit tests, you’re actually testing the
database adapter and abstraction layer, in this case: ActiveRecord, and
not the logic that you have written.

That is commonly believed, but the situation is IMHO not that
open-and-shut. Read on.

ActiveRecord, has been tested
extensively, it would be redundant to test it again. By removing the
database from the testing, you can focus on testing your business
logic…

Not always. Often, removing the DB from testing makes it harder to
focus on testing the logic.

Some examples may be helpful here. Of course, it’s asinine for me to do

User.create! :name => ‘John’
User.find_by_name(‘John’).should_not be_nil

as that would indeed be testing ActiveRecord. But consider

class Sandwich
def make_blt
Ingredient.create(:name => ‘bacon’)
Ingredient.create(:name => ‘lettuce’)
Ingredient.create(:name => ‘tomato’)
end
end

I think it is entirely reasonable to test this with

describe Sandwich
describe ‘make_blt’
it “should create bacon, lettuce, and tomato” do
Sandwich.make_blt

  ['bacon', 'lettuce', 'tomato'].each do |name|
    Ingredient.find_by_name(name).should_not be_nil
  end
end

end
end

because this is not testing ActiveRecord; rather, it’s testing that I
made the proper calls to ActiveRecord.

To be sure, in this case, I could just as easily do
Ingredient.should_receive(:create).with(:name => ‘bacon’), but that’s
only possible because the method is so simple, and to my mind it’s a
little smelly anyway: since the method being tested should be treated as
a black box, I’d rather verify the final state than chase all the method
calls that got us there. And the only way to verify the final state is
to have something tracking that state – like, oh, say, a DB.

and you have the side benefit of it being quicker, which is
essential for TDD to be a practical way to develop software.

An in-memory DB has similar benefits while not discarding state.

I think
what you’re describing is considered to be functional testing or
integration test, not unit testing.

The test I wrote above would certainly be considered a unit test.

[…]

This is one of the rare times when I think Ruby got it wrong when
compared with other languages, most other more mature languages Java,
C++, etc… provide tools to avoid hitting the database during unit
testing for just this reason.

This is a Rails and ActiveRecord issue, not a Ruby one.

I think Rails, and Ruby in general is
heading in this direction too. The next generation of ORM databases like
DataMapper have this sort of thing built in.

DataMapper is an ORM, not an “ORM database”. In what respect does
DataMapper’s testing differ here? (I’ve never used DataMapper, though
it certainly looks interesting.)

Hope this helps!

Only by tending to confirm that some of the argument here is fallacious
(unless Avdi has a better one).

Cheers,
Eric

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

Marnen,

You make some interesting points… I think I still have a ton to learn
about unit testing and TDD best practices. I’m still just getting
started. Thanks for the info!

Warmest Regards,
Eric

I’m fairly new to unit testing and TDD, but I think I understand the
concept of what they’re talking about here. The point is that if you are
writing to the database during unit tests, you’re actually testing the
database adapter and abstraction layer, in this case: ActiveRecord, and
not the logic that you have written. ActiveRecord, has been tested
extensively, it would be redundant to test it again. By removing the
database from the testing, you can focus on testing your business
logic… and you have the side benefit of it being quicker, which is
essential for TDD to be a practical way to develop software. I think
what you’re describing is considered to be functional testing or
integration test, not unit testing. Functional and integration testing
is equally important, it just occurs at a different level.

This is one of the rare times when I think Ruby got it wrong when
compared with other languages, most other more mature languages Java,
C++, etc… provide tools to avoid hitting the database during unit
testing for just this reason. I think Rails, and Ruby in general is
heading in this direction too. The next generation of ORM databases like
DataMapper have this sort of thing built in. Hope this helps!

Cheers,
Eric

Oh, and I almost forgot to mention that Factory_Girl does this as well.
Using Factory.build() you only write to memory and not the database.
Factory_Girl can also allow writing to the database for functional and
integration testing, so you get the best of both worlds. It’s one of the
pieces in my testing suite.

-Eric

Avdi G. wrote:

  1. Why are we suddenly discussing a two year old post here? Clue me in.

My apologies! It appeared at the top of my list feed and I neglected to
check the date. I’m not sure why it appeared at the top of my feed.

[…]

But briefly, I’ll say this: if you are including the DB, you aren’t Unit
Testing. Period. Your tests might be useful and important, but they
aren’t Unit Tests. A lot of Rails practices, while overall encouraging
more TDD, have muddied the waters WRT to what is a Unit Test.

Yeah, I’m aware of that.

A Unit
test isolates a single object or method from all of its
collaborators, and tests the inputs of that object or method in a
vacuum. If it doesn’t isolate, it’s not a Unit Test.

And there’s an argument to be made that since the AR framework is part
of an AR class, it must be respected – and hit some state-preserving
DB-like thing – to properly unit test an AR class.

Which, again, is not to say it’s a bad test. These days I drive my app
development almost exclusively from high-level full-stack Cucumber
acceptance tests, and only drop down to the Unit level when there is
some particularly interesting/tricky logic to be described.

I do similarly.

And
sometimes I write functional level tests that include the DB but not the
UI level. But I don’t call them unit tests and I keep them separate from
my unit tests. If you are interested in this kind of Unit Test
discipline - which, when followed, tends to make for smaller,
tightly-cohesive objects - NullDB may be for you.

Got some sample code in this style? I’d like to look at it, even though
I think (a priori) that it is a recklessly dangerous way to test AR
classes. I just might learn something. :slight_smile:

Especially because it
enables you to test (for instance) logic in after_save hooks which would
otherwise force the DB to get involved.

I want the DB to get involved if I’m testing the DB lifecycle of my
objects.


Avdi

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]

  1. Why are we suddenly discussing a two year old post here? Clue me in.

  2. Myron Marsten is now the maintainer of NullDB. See
    GitHub - nulldb/nulldb: An ActiveRecord null database adapter for greater speed and isolation in unit tests.

  3. I’m not really interested in rehashing the arguments for NullDB.
    Enough teams are happily using it that I had to transfer maintainership
    over to someone else so that I could focus on other projects. I suggest
    you go on the RSpec-User’s mailing list and ask if someone wants to
    explain why they use it.

But briefly, I’ll say this: if you are including the DB, you aren’t Unit
Testing. Period. Your tests might be useful and important, but they
aren’t Unit Tests. A lot of Rails practices, while overall encouraging
more TDD, have muddied the waters WRT to what is a Unit Test. A Unit
test isolates a single object or method from all of its
collaborators, and tests the inputs of that object or method in a
vacuum. If it doesn’t isolate, it’s not a Unit Test.

Which, again, is not to say it’s a bad test. These days I drive my app
development almost exclusively from high-level full-stack Cucumber
acceptance tests, and only drop down to the Unit level when there is
some particularly interesting/tricky logic to be described. And
sometimes I write functional level tests that include the DB but not the
UI level. But I don’t call them unit tests and I keep them separate from
my unit tests. If you are interested in this kind of Unit Test
discipline - which, when followed, tends to make for smaller,
tightly-cohesive objects - NullDB may be for you. Especially because it
enables you to test (for instance) logic in after_save hooks which would
otherwise force the DB to get involved.


Avdi

Marnen Laibow-Koser wrote:

And there’s an argument to be made that since the AR framework is part
of an AR class, it must be respected – and hit some state-preserving
DB-like thing – to properly unit test an AR class.

This is the kind of muddling of concepts that I think Rails encourages.
The idea that “Model == Database Thingy”. At every opportunity I
encourage developers to start their apps with bare, pure Ruby models and
only add “< ActiveRecord::Base” in a later iteration. This practice
helps to start thinking of your objects purely in terms of how they
model your domain - which is how OO is supposed to work. After you spend
some time looking at the models strictly in terms of their capabilities
and responsibilities, then you add in persistence. Usually this means
that you add a collaborator, which is some kind of OO/RDB mapping
system. But it’s still just another collaborator, with neat, clean
division of responsibilities.

Of course, AR makes it harder to think of it as a collaborator because
you have to inherit from it and it adds all sorts of methods to self and
imposes arbitrary rules like “no initializers”. But if you are careful
you can still think of it as a (rather pushy) collaborator and structure
your tests accordingly - separating the tests of persistence from the
tests of logic.

I’ll add a post about the how and the why and the benefits of this kind
of strict separation to my queue on Virtuous Code.


Avdi

Avdi G. wrote:

Marnen Laibow-Koser wrote:

And there’s an argument to be made that since the AR framework is part
of an AR class, it must be respected – and hit some state-preserving
DB-like thing – to properly unit test an AR class.

This is the kind of muddling of concepts that I think Rails encourages.
The idea that “Model == Database Thingy”.

That’s not an idea that I have. I used to do MVC without an ORM (and
indeed, without “official” objects), and I’m quite used to writing
non-AR models in Rails.

At every opportunity I
encourage developers to start their apps with bare, pure Ruby models and
only add “< ActiveRecord::Base” in a later iteration.

That seems like more trouble than it’s worth with Rails, but I’ll think
about how I’d try that.

This practice
helps to start thinking of your objects purely in terms of how they
model your domain - which is how OO is supposed to work.

No argument there.

[…]

Of course, AR makes it harder to think of it as a collaborator because
you have to inherit from it and it adds all sorts of methods to self and
imposes arbitrary rules like “no initializers”. But if you are careful
you can still think of it as a (rather pushy) collaborator and structure
your tests accordingly - separating the tests of persistence from the
tests of logic.

Again, I do that anyway. But if I’m testing features that touch the
persistence layer, then I want an actual persistence layer to talk to,
not just a black hole. And the way Rails structures its objects, I
think that’s still a unit test.

If Rails worked a bit differently and used a separate persistence
library instead of an ORM, then I’d agree with you on the appropriate
way to test AR objects. But it doesn’t, so I think I don’t.

I’ll add a post about the how and the why and the benefits of this kind
of strict separation to my queue on Virtuous Code.

I will be interested to read that. At the moment, this separation just
seems like fighting Rails to no advantage and some potential harm.


Avdi

Best,

Marnen Laibow-Koser
http://www.marnen.org
[email protected]