Forum: Ruby help with minitest - module testing

Ccd9be37f5bd322e9d3968f767328928?d=identicon&s=25 Sebastjan H. (sebastjan_h)
on 2014-02-21 11:06
Hi,

I need advice and help regarding the testing with minitest(/spec). More
precisely I need help with testing the module methods. These are
basically blocks I've put in a module to organize my code. Following
some tutorials I've learned to test classes and their methods, but I'm
stuck with module methods.

I'm refactoring a program and I'm learning testing with minitest along
the way. My goal is to adopt the TDD or BDD style eventually. However
most of the code for this program is already in place. The program is a
computer aided translation tool, which stores translations in
YAML::Store file based databases. I use Shoes for the GUI.

As this is out of context, here are the variables definitions:

1.  tms_database: the database (YAML::Store) which holds all translation
memories

2.  new_tm_name: the name of the new database which is an input from the
user

3.  all_tms: the listbox (Shoes element) which holds all translation
memories' names from the tms_database


Here is the actual code snippet:

#module file: databases_module.rb

module DatabaseModule

  class TranslationMemory
    attr_accessor :name

    def initialize(new_tm_name)
      @name = new_tm_name
    end
  end


  def new_translation_memory(tms_database, new_tm_name, all_tms)
    #create new translation memory
    translation_memory = TranslationMemory.new(new_tm_name)

    #store new translation memory
    tms_database.transaction do
      tms_database[translation_memory.name] = translation_memory
    end

    # repopulate the listbox with all translation memories including the
newest one
    tms_database.transaction(true) do
      all_tms.items = tms_database.roots
    end
  end

end

----------------------------------------------------------
#the spec file: databases_module_spec.rb

require 'minitest/spec'
require 'minitest/mock'
require 'minitest/autorun'
require_relative "../lib/WordFisher/databases_module"
include DatabaseModule


describe DatabaseModule do

  describe "create new translation memory" do

    before do
      @translation_memory = TranslationMemory.new("TM")
      @tms_database = MiniTest::Mock.new
      @tms_database.expect(:transaction,
DatabaseModule::TranslationMemory)
      @all_tms = MiniTest::Mock.new
      @all_tms.expect(:items, ["asdasdas"])
    end


    it "creates a new translation memory with a name" do
      @translation_memory.name.wont_be_empty
    end

    it "saves the new translation memory to DB for all database" do
      @tms_database.transaction do
        @tms_database[@translation_memory.name].must_be_kind_of
        DatabaseModule::TranslationMemory
      end
    end

    it "populates the array for all translation memories" do
      @all_tms.items.wont_be_empty
    end

  end

end
-----------------------------------------------------------------

My question is what am I doing wrong? I know I must be doing something
wrong because the test pass even if I comment out the second and the
third part of the new_translation_memory method (ie: storing the new TM
and populating the listbox).
The overall question that bugs me, how to test these methods/blocks
which don't get called on an object? I just pass them to the button:

button "New TM" do
new_translation_memory(tms_database, new_tm_name, all_tms)
end


thank you and kind regards,
seba
5a837592409354297424994e8d62f722?d=identicon&s=25 Ryan Davis (Guest)
on 2014-02-21 23:48
(Received via mailing list)
On Feb 21, 2014, at 2:06, Sebastjan H. <lists@ruby-forum.com> wrote:

> My question is what am I doing wrong?

Easy: mocks. Explanation below.

>    before do
>      @translation_memory = TranslationMemory.new("TM")
>      @tms_database = MiniTest::Mock.new
>      @tms_database.expect(:transaction,
> DatabaseModule::TranslationMemory)
>      @all_tms = MiniTest::Mock.new
>      @all_tms.expect(:items, ["asdasdas"])
>    end

Rule #1: fail FIRST. If you don't fail, how do you ever know your test
even works?
Rule #2: mock LAST. Better yet, don't mock at all if you can help it.

>    it "creates a new translation memory with a name" do
>      @translation_memory.name.wont_be_empty
>    end

This only tests that you wrote attr_accessor and hooked it up in the
initializer. Low value. Even lower that you're only testing that the
value is non-empty. You could have returned `[1]` for all you know.

>    it "saves the new translation memory to DB for all database" do
>      @tms_database.transaction do
>        @tms_database[@translation_memory.name].must_be_kind_of
>        DatabaseModule::TranslationMemory
>      end
>    end

This just tests that you wrote a mock that returns transaction. Worse
yet, your mock returns a TranslationMemory and ignores your block
entirely. You can probably verify that via the assertion count.

>    it "populates the array for all translation memories" do
>      @all_tms.items.wont_be_empty
>    end

This just tests that you wrote a mock that returns items. Again, `[1]`
passes.
Ccd9be37f5bd322e9d3968f767328928?d=identicon&s=25 Sebastjan H. (sebastjan_h)
on 2014-02-23 19:14
Hi,

thank you for the detailed feedback.

I have one additional question. I found a suggestion (for testing with
RSpec) that modules can be tested using dummy class which extends the
module to be tested.

Would this be better than mocks?

regards,
seba
Please log in before posting. Registration is free and takes only a minute.
Existing account

NEW: Do you have a Google/GoogleMail, Yahoo or Facebook account? No registration required!
Log in with Google account | Log in with Yahoo account | Log in with Facebook account
No account? Register here.