Assuming a shared method like:
def sanitize_spaces(dirty_attribute)
dirty_attribute = dirty_attribute.gsub(/\s+/, "")
end
what kind of a minitest should be written? Naturally, one could test the method that calls this method, but that sort of buries the test within another. The goal is to have a mechanism to test this individual method on its own.
I don't think this method should be public (presumably you're just trying to give a name to a piece of logic so that it is easier to read), and if it is not public then I would not test it (you're testing implementation details).
But if the sanitize code you give is an example of some larger piece of logic, or it's a widely used helper method you could do something like this:
(This code assumes that the method is wrapped in a class called MySanitizer)
require "minitest/autorun"
class TestMySanitizer < Minitest::Test
def setup
#sanitizer = MySanitizer.new
end
def test_that_return_value_is_correct
string_to_be_sanitized = "O H A I \t\t\n !"
assert_equal "OHAI!", sanitizer.sanitize_spaces(string_to_be_sanitized)
end
def test_that_nowhitespace_input_is_unaffected
nowhitespace = "OHAI!"
assert_equal nowhitespace, sanitizer.sanitize_spaces(nowhitespace)
end
end
Theoretically you could go further and have tests that include every form of whitespace and non-whitespace character, but your testing (automated or otherwise) is unlikely to be better than that which is already in place in the Ruby standard library for gsub, so it is not particularly useful to verify anything beyond the fact that there is something there which affects whitespace.
Related
what is right way of writing module? is it only used to stock some peace of code to minimize the number of lines, or is it something much more important than that
I have used and seen ways of writing module, I am working on setting up correct way to define and standardised module. this example is kind of controller code that we use in rails
Way 1 :-
module B
extend ActiveSupport::Concern
def process_items
# do somthing...
#items.pluck(:names)
end
end
Class A
include B
def index
#items = Item.all
#item_names = process_items
end
end
Way 2 :-
module B
extend ActiveSupport::Concern
def process_items(items)
# do somthing...
items.pluck(:names)
end
end
Class A
include B
def index
#items = Item.all
#item_names = process_items(#items)
end
end
Way 1 :-
When I see this independently, its not much readable as I don't know how #items appeared in this method
Unit testing would be hard for method as its dependent
Way 2 :-
Looking at method I can see input is coming we are processing it and returning it back (readablity is good)
Unit testing is easy to this, we wll call method pass what it needs and expect
The way I see modules should be independent, self explanatory, it should be generic so that can be used in any class, kind of helpers. But other way could be dependent on where we use modules
We are using modules like in rails
We use conccern in models, when we call module method we can use self.<field> we don't need to pass anything because instance variable is supposed to be accesssable in every instance method
View helpers are modules I see they put logic into it hard to understand how the variable come from may be instance variable or params, what about making it method which accept somthing and return it back
Concerns on controllers, like the example I have given
I would like to have thoughts on this, what is best approach out of it? is it something which can be standarise or it is more situational or I don't know yet :)
Note: -
I was looking at this question but answer given on this question is no more valid as referenced links are not working.
Right Way to Use Module
The difference here is practically academic, as if you have attr_reader :x then both #x and x will have the same meaning.
It's understood that within a mixin module you will be referencing methods and/or variables that are part of the class or module doing the "mixing in". As such, seeing #x, or in your case, #items, should not come as a real surprise.
If you want to add it as an explicit argument you're sort of missing a lot of the benefits of using a mixin in the first place. You don't need to mix it in at all, you can just use it like B.process_items(...). In other words, your second approach is having an identity crisis. Is it a stand-alone module that includes Concern for no reason, or a weak mixin?
When it comes to testing, you must test the mixin in a module or class which implements the required features. In this case you need either an #items variable, or an items method, and that must have a value of the expected type.
This should be documented somewhere for clarity, but is effectively an implicit contract with anyone using this module.
I'm using let(:foo) { create_foo() } inside my tests. create_foo is a test helper that does some fairly time expensive setup.
So everytime a test is run, foo is created, and that takes some time. However, the foo object itself does not change, I just want to unit test methods on that object, one by one, seperated into single tests.
So, is there a RSpec equivalent of let to share the variable across multiple examples, but keep the nice things like lazy loading (if foo isn't needed) and also the automatic method definition of foo so that it can be used in shared examples, without referencing it with a #foo?
Or do I have to simply define a
def foo
create_foo()
end
Can you just put it in shared examples but use memoization?
def foo
#foo ||= create_foo()
end
Using let in this way goes against what it was designed for. You should consider using before_all which runs once per test group:
before :all do
#instancevar = create_object()
end
Keep in mind that this may not be wise if create_object() hits a database since it may introduce coupling and maintain state between your tests.
How do I test a method available only to an ActiveRecord relation proxy class in rspec? Like for example sum which would look something like #collection.sum(:attribute)
Here is what I'm trying to do:
#invoice = stub_model(Invoice)
#line_item = stub_model(LineItem, {quantity: 1, cost: 10.00, invoice: #invoice})
#invoice.stub(:line_items).and_return([#line_item])
#invoice.line_items.sum(:cost).should eq(10)
This doesn't work because #invoice.line_items returns a regular array that doesn't define sum in the same way as an ActiveRecord::Relation object does.
Any help is greatly appreciated.
I'm not sure which Rails you are on so I'll use Rails 4.0.x for this example; the principle still holds for Rails 3.x.
TL;DR: You don't want to take this route.
Consider not stubbing model specs
Consider adding domain specific APIs
You are rapidly heading down the road of over mocking/stubbing. I have been down this road, it does not lead to fun. Part of all of this comes down to violating the Law of Demeter. Part of it comes down to using the Rails APIs instead of creating your own domain APIs.
When you request an relation collection from an ActiveRecord model it does not return an Array as you are aware. In Rails 4.0.x, with a has_many association, the class which is returned is: ActiveRecord::Associations::CollectionProxy::ActiveRecord_Associations_CollectionProxy_Model.
Issue #1: Stubbing the wrong return value
Here your return type is an Array. While the actual return type is the ActiveRecord_Associations_CollectionProxy_Model. In stub/mock land, this isn't necessarily a bad thing. However, if you intend to use other calls on the object returned by the stub they need to match the same API contracts. Otherwise, you're not stubbing the same behavior.
In this case, the sum method defined on the AR association proxy actually executes SQL when it runs. The sum method defined on Array is patched in via Active Support. The Array#sum behavior is fundamentally different:
def sum(identity = 0, &block)
if block_given?
map(&block).sum(identity)
else
inject { |sum, element| sum + element } || identity
end
end
As you can see, it sums the elements, not the sum of the requested attribute.
Issue #2: Asserting on your stub'd object
The other main problem you have, is you are attempting to spec that you're stub returns what you stubbed. This doesn't make sense. The point of a stub is to return a canned answer. It's not to assert on how it behaves.
What you wrote isn't fundamentally different from:
invoice = stub_model(Invoice)
line_item = stub_model(LineItem, {quantity: 1, cost: 10.00, invoice: invoice})
invoice.stub(:line_items).and_return([line_item])
invoice.line_items.should eq([line_item])
Unless this is supposed to be a sanity check, it adds no real value to your specs.
Suggestions
I'm not sure what type of spec you are writing here. If this is a more traditional unit test or an acceptance test, then I probably wouldn't stub anything. There isn't necessarily anything wrong with hitting a database at times, especially when the thing you are testing is how you interact with it; which is really what you are doing here.
Another thing you can do is start to use this to create your own specific domain model APIs. All this really means is defining interfaces on objects that make sense for your domain, which may or may not be backed by a DB or other resource.
For example, take your invoice.line_items.sum(:cost).should eq(10), this is clearly testing the Rails AR API. In domain terms it means nothing really. However, invoice.subtotal probably means a lot more to your domain:
# app/models/invoice.rb
class Invoice < ActiveRecord::Base
def subtotal
line_items.sum(:cost)
end
end
# spec/models/invoice_spec.rb
# These are unit specs on the model, which directly works with the DB
# it probably doesn't make sense to stub things here
describe Invoice do
specify "the subtotal is the sum of all line item cost" do
invoice = create(:invoice)
3.times do |i|
cost = (i + 1) * 2
invoice.line_items.create(cost: cost)
end
expect(invoice.subtotal).to eq 12
end
end
Now later, when you use Invoice in some other part of your code, you can easily stub this if you need to:
# spec/helpers/invoice_helper_spec.rb
describe InvoiceHelper do
context "requesting the formatted subtotal" do
it "returns US dollars to two decimal places" do
invoice = double(Invoice, subtotal: 1012)
assign(:invoice, invoice)
expect(helper.subtotal_in_dollars).to eq "$10.12"
end
end
end
So when it is ok to stub model specs? Well, that's really a judgement call, and will vary from person to person, and code base to code base. However, just because something is in app/models doesn't mean it has to be an ActiveRecord model. In those cases, it's potentially fine to stub domain APIs on collaborators.
EDIT: create vs build
In the example above I used create(:invoice) and invoice.line_items.create(cost: cost). However, if you are concerned about DB slowness, you probably could just as easily use build(:invoice) and invoice.line_items.build(cost: cost).
Be aware that my use of create(:invoice) and build(:invoice) here is in reference to generic "factories", not a reference to a specific gem. You could simply use Model.create and Model.new in their place. Additionally, the line_items.create and line_items.build are provided by AR and have nothing to do with any factory gems.
I'm very new on TDD and unit-testing, and I'm having quite a lot of doubts about the correct approach I should take during the tests of the custom model validations.
Suppose I have a custom validation:
User < ActiveRecord::Base
validate :weird_validation
def weird_validation
# Validate the weird attribute
end
end
Should I take this approach:
context "validation"
it "pass the validation with weird stuff" do
user = User.new weird: "something weird"
user.should be_valid
end
it "should't pass the validation with normal stuff" do
user = User.new weird: "something"
user.should_not be_valid
user.errors[:weird].size.should eq 1
end
end
Or this one:
context "#weird_validation" do
it "should not add an error if weird is weird" do
user = User.new
user.stub(:weird){"something weird"}
user.errors.should_not_receive :add
user.weird_validation.should eq true
end
it "should add an error if weird is not weird" do
user = User.new
user.stub(:weird){"something"}
user.errors.should_receive(:add).with(:weird, anything())
user.weird_validation.should eq false
end
end
So IMHO
The first approach
Pros
It test behaviour
Easy refactoring
Cons
Dependable of other methods
Something unrelated could make the test fail
The second approach
Pros
It doesn't relay on anything else, since everything else is stubbed
It's very specific of all the things the code should do
Cons
It's very specific of all the things the code should do
Refactoring the validations could potentially break the test
I personally think the correct approach should be the first one, but I can't avoid to think that I'm relying too much in other methods rather than the one I want to test, and if the test fails it may be because of any method withing the model. For example, it would not validate if the validation of other attribute failed.
Using the second approach I'm practically writing the code twice, which seems like a waste of time and effort. But I'm unit-testing the isolated method about what it should do. (and I'm personally doing this for every single method, which is very bad and time consuming I think)
Is there any midway when it comes to using stubs and mocks? Because I've taken the second approach and I think I'm abusing it.
IMO the second approach is the best one because you test your model properties and validations one at a time (the "unit" part).
To avoid overhead, you may consider using shoulda. It is really efficient for models unit testing. We're usually using a factory_girl/mocha/shoulda combination for functional testing (factory_girl and mocha are also very helpful to test queries and named scopes). Tests are easy to write, read and maintain :
# class UserTest < ActiveSupport::TestCase
# or
# describe 'User' do
should have_db_column(:weird).of_type(:string).with_options(:limit=>255)
should allow_value("something weird").for(:weird)
should_not allow_value("something").for(:weird)
should ensure_length_of(:weird).is_at_least(1).is_at_most(255)
# end
Shoulda generates positive/negative matchers therefore avoids a lot of code duplication.
I'd like to, for all helpers, across my entire Rails application, replace this syntax:
time_ago_in_words(#from_time)
with this:
#from_time.time_ago_in_words
Ideally this change would also make the helpers available anywhere in my application (the same way, say, 5.times is).
Does anyone know of a plugin that does this? Would it be difficult to roll my own?
If you create a new model you can make it inherit the methods from an existing class.
Example (app/models/mykindofstring.rb):
class Mykindofstring < String
def all_caps_and_reverse
self.upcase.reverse
end
end
Then from the controller:
#my_string = Mykindofstring.new('This is my string')
and finally calling it from the view:
<%= #my_string %>
displays as This is my string, while:
<%= #my_string.all_caps_and_reverse %>
displays as GNIRTS YM SI SIHT.
The class inherits from the string class, so all the methods that apply to a string also apply to a mykindofstring object. The method in it is just a new name for a combination of two existing string methods, but you can make it do whatever you want, such as a method to convert a time into a formatted string.
Basically it's just extending a current Ruby class with your own methods instead of using functions/helpers.
Major Edit
Based on Ians comment to this answer and his own answer to the above question, I played around a bit in Rails and came up with adding this to the application_helper.rb:
module ApplicationHelper
String.class_eval do
def all_caps_and_reverse
self.upcase.reverse
end
end
end
Now just call the method on any string in the app:
#string = 'This is a string.'
#string.all_caps_and_reverse
.gnirts a si sihT
You won't find any easy way to do this across your whole application for all (or even most) helpers.
Helpers are structured as modules meant to be included in view rendering classes. To leverage them, you'd have to keep a copy of an ActionView around (not a HUGE deal I suppose).
You can always open up the classes and specify each helper you want, though:
class Time
def time_ago_in_words
ActionView::Base.new.time_ago_in_words self
end
end
>> t = Time.now
=> Tue May 12 10:54:07 -0400 2009
>> t.time_ago_in_words
=> "less than a minute"
(wait a minute)
>> t.time_ago_in_words
=> "1 minute"
If you take that approach, I recommend caching the instance of ActionView::Base that you use. Or, if you don't want to go so heavyweight, you can create your own class to just include the helpers you want (you don't want to include the helpers directly in each Time, Date, String, etc, class, though, as that will clutter up the method namespace pretty fierce -- and conflict with the natural names you want).
If you are using Ruby 2.1 you can use Refinements. They are an alternative to monkey-patching Ruby's classes. to borrow #Jarrod's monkey-patch example:
module MyString
refine String do
def all_caps_and_reverse
upcase.reverse
end
end
end
You can now use this in any object you want:
class Post
using MyString
end
#post.title.all_caps_and_reverse
You can read more about Refinements here.
Make a model, TimeAgo or something similar. Implement following this idea here:
a code snippet for "wordifying" numbers
Then, in your controller, create the instance variables using this class. Then, in your view, call #from_time.in_words