And if you could could go ahead and get a can of pesticide and take care of the roach problem we’ve been having that would be great.
While as a general rule testing log output can make tests brittle
because logs are free text and can change easily, there are certainly
cases where testing them makes sense.
Our application was missing some really important business
flow logging that we discovered was needed when we attempted to
troubleshoot a production issue. We had to spend a significant amount of
time hunting something down that would have been trivial to find in a
well formatted info log message. I added that logging to the
application and then wanted to make sure these business level log messages
were being emitted at the right places in the application.
To test log messages in Padrino, I added the following helper to our
spec/support/spec_helpers.rb file:
spec/helpers/spec_helpers.rb
1234567891011121314151617181920
# Pass a block and log output will be captured in that block and# returned as an array of lines for testing.defcapture_logger_outputlogger.flush# This causes messages to be captured in the internal logger array# that can be accessed via logger.buffer.logger.auto_flush=false# Run user's codeyield# Save the log lines captured during the test block# NOTE: have to clone otherwise flush clears the referred to arraybuffer=logger.buffer.clonelogger.flushlogger.auto_flush=truebufferend
Now in your tests you can run test code and capture any log messages that
happened during the code run!
Stan, Chotchkie’s Manager: Now, you know it’s up to you whether or not you want to just do the bare minimum. Or… well, like Brian, for example, has thirty seven pieces of flair, okay. And a terrific smile.
Forget Brian, when it comes to writing rspec tests let your lazy come
out. Do the minimum. Write a test that just does enough to prove
that your feature or component behaves as expected.
Do
Test all known user story / requirement business paths ( “happy paths” )
related to the component or feature.
Write a test that really exercises them with real data ( not mocks
unless you have no other choice but to use mocks ).
Write it so it both proves your code does what it is supposed to do
and so that it teaches other developers looking at it how that part of
the application or component should behave
Don’t
Write a placeholder or filler test just so you can say you have test
coverage
Validate every single attribute or piece of data related to the test;
that makes your test very tightly coupled to the implementation and
means that any tiny change to the code requires the test to be updated
as well. This makes developers hate you and your overly high-maintenance tests.
Be obscure nor clever. Make the test direct and simple – the way we’d
all like our English to be!
Use mocks unless you absolutely need to. Why? Mocks quickly become
lies – if they are flexible enough to take made up method names they are
likely to continue to take that made up method name even when the real
implementation changes. Now your test lies to everyone when it says
it passes because what it tests is something that is not used in the real
implementation. The only place I personally feel comfortable with
mocks is when external network-based services are dependencies and
having them reliably be available in development and test environments
is not possible.
We use Dalli for memcached-based route caching with
Padrino 0.12. I wanted to add rate throttling for all /api
route handlers to our app and wanted to use Dalli for that as well.
I found Rack::Attack, a
really nice Rack-based gem that allows for connection throttling,
blacklisting and whitelisting of clients. Cool stuff, perfect for our
needs. Except it expects the caching layer to conform to the
ActiveSupport::Cache::Store interface:
Note that Rack::Attack.cache is only used for throttling; not blacklisting & whitelisting. Your cache store must implement increment and write like ActiveSupport::Cache::Store.
Darn! Oh, wait, not so bad. But I don’t want to have to do inline
creation of the client etc in app/app.rb, especially because I’m likely
to have different throttling requirements for different environments.
Modules and blocks to the rescue!
Create a subclass for the Dalli client that conforms to the ActiveSupport API.
Create a helper method that can be used inline in app/app.rb that
takes a block with an environment-specific policy.
Wrap the use and cache client creation so it doesn’t have to be
repeated in app/app.rb
Our security mixin module with configure_security helper
moduleSecurityrequire'dalli/client'@@store=nil# Dalli client does not conform to cache client# interface Rack::Attack expects - so add wrappers# for methods.classMemStore<Dalli::Clientdefdecrement(key,step,options)decr(key,step,options[:expires_in])enddefincrement(key,step,options)incr(key,step,options[:expires_in],nil)enddefwrite(key,value,options)# Have to use raw => true for write or we get marshall# errors as Dalli marshals the Fixnum object instead of# sending the raw integer valueset(key,value,options[:expires_in],:raw=>true)endenddefinitialize_storeif@@store.nil?useRack::Attack@@store=Rack::Attack.cache.store=MemStore.new%{localhost:11211}endend# Called from app/app.rbdefconfigure_securityinitialize_storeyield(Rack::Attack)endend
Now use it in app/app.rb
app/app.rb
1234567891011121314
self.extendSecurity# lib/security.rb - adds configure_security methodconfigure:demo,:development,:apido# Rate limit calls to /api URLs to 2 a second in all envs.configure_securitydo|client|client.throttle('req/ip',:limit=>2,:period=>1.second)do|req|# If we return false nothing is cached ( no rate limiting )# - only rate limit calls to /api URLsreq.path.index(%{/api}).nil??false:req.ipendendend
Most java developers have a really solid grasp of object-oriented
concepts and patterns, which makes the transition to ruby easy really easy
from that perspective. Moving from a statically typed to dynamic
language is still a big change and some of the more basic coding practices
change significantly. Here are some recommendations I’ve come up with
based on my experiences doing mentoring with a bunch of java developers
transitioning to ruby on our team and some code patterns I’ve heard used
by a colleague of mine who is also mentoring some java developers who
are transitioning to ruby.
Hints
Minimize the use of class variables – ruby’s class variables have some
strange inheritence rules, and, with multi-process web containers,
they don’t act as singletons across all instances of an app on the
same server anyway.
Prefer local variables over instance variables when possible.
Minimizing the scope of a variable in a dynamic language reduces the
possibility of bugs and makes it much easier to see where the variable
is being set and used.
Don’t pre-initialize data structures – no need. Arrays and hashes are
dynamic and in the vast majority of circumstances pre-sizing them does
not help performance and just leads to more noise in your code.
Snake-case variables .. thing_one and thing_two.
Semi-colons are not needed at the ends of statements ; only use them
when doing single-line method definitions or rare cases where you want
two statements on the same line.
Prefer composition over complex inheritence trees.
Prefer mixins over complex inheritence trees.
Tips
Explore code with irb or Pry. No faster way to
get conversant with ruby than to explore using it from a ruby-based
shell!
Recently at my job our teams have grown to include a number of people
who are not co-located with us. Here are some of the tools we’ve been
using that have helped us develop and design together across locations.
Social coding
Gitlab – Github-like UI on top of git. Many of
the same features of Github, pretty easy to host internally.
Remote pair programming
Screenhero – by far the best remote
graphical pair-programming tool I’ve found – includes streaming audio
support so we don’t have to have Google Hangouts, Skype, or another
audio application open as well.
Hastebin – a paste bin
tool that runs on node and is easy to set up internally – fast, low
memory usage, zero maintenance.
Remote design
ASCII Flow – java-based, open source
ASCII diagramming tool. Great for cutting and posting diagrams into
markdown files. Haven’t tried installing it internally yet.
Etherpad – near-real time collaborative,
modeless editing tool – like Google docs, highlights each person’s
text in a different color. Runs on node as well.
Remote communication
We’ve heard a lot about various team-oriented chat tools, for now we’ve
been sticking to the basics and so far so good.
When a ruby application runs in a multi-process web container (phusion passenger or puma for example), class variables set in one
instance of the application will not be visible in another instance – so if you use them be very aware that the setting will purely
apply to the instance of the application in which the variables are set.
If you need to share data across instances of an application, consider using memcache or another host-level data storage mechanism
with a library that makes it easy to get and set data ( like Dalli for ruby ).
Always return true or false at the end of the handler; true to allow additional events to fire, false to stop event propogation. If you do not oh boy will you get very strange behavior. For example, in recent Firefox releases you will get an error thrown with HTTP status 0 and message “Error,” yep, that is all you get.
if you have mutiple elementa trigger the same event and the only difference is data ( for example a list of names where clicking posts the name ), write a handler based off of a CSS class selector. Do not create one handler per row or item using unique div IDs as the CSS selector – this eats browser memory quickly!
Save jQuery elements in function scoped variablea and chain events from them – doing $( “selector” ) calls for the same element over and over again is expensive time and memory wise and slows your code down.
Pry is a really powerful replacement for IRB – I love using it with Rails. I recently started working with Padrino and of course had to have it working there as well.
Pry will read and execute a .pryrc file that exists in the current directory as it boots – here is my Padrino specific .pryrc file – it boots your app just like you’d get in Rails using Pry:
We add in code that allows us to send mock parameters to controller actions via Capybara tests for all controllers while testing -so we can simulate session state ( for example – user being logged in ). In Rails you do this by re-opening ApplicationController in spec/spec_helper.rb and adding in a before_filter. In Padrino you can do this by adding custom code to app.rb in a before block – the before block is called for every controller action.
app/app.rb
1234567891011
configure:testdobeforedoparams.keys.eachdo|param|ifparam=~/^mock_/mock_param=param.gsub(/mock_/,'')session[mock_param]=params[param]logger.debug%{ #{mock_param} set to #{params[param]}}endendendend