Tuesday, December 28, 2010

Random Cucumber/Ruby Things I Learned

Some random stuff I learned as a "seasoned" Java developer, working in Ruby and more specifically Cucumber for the first time this year, and not having a clue as to what I was doing. I realize some of this stuff will be mind numbingly obvious to "seasoned" Ruby devs, but thought it would be interesting to share regardless.

Some background. On my project some "legacy" Cucumber tests existed. Legacy, you ask? How can Cucumber tests be legacy? Simple, they were overly fragile, and violated DRY over and over again. Therefore, since my overall group has chosen Cucumber as the ATDD tool of choice (and by extension Gherkin as the Story card language of choice), it was a good idea to get in and refactor the tests. I had a good example in how to do this in the blog posts by Jeff Morgan, and I fortunately had someone who I could tap on the shoulder on a sister team in Joel Helbling. So with the goal of learning some Ruby in mind, and a copy of RubyMine I began tearing up the soil and planting cukes.

Firstly, when you're working with multiple versions of Ruby, in my case, JRuby and Ruby for Windows, you might need to modify your Gemfile by the platform. In my case, I needed JRuby to run the tests the same way as the legacy tests were running. JRuby drove Celerity (yay HtmlUnit!) tests on Hudson, while native Ruby drives Waitr/FireWatir browser driven versions of the same tests.

So, although it's very obvious after the fact, here's a clue...there is no Celerity for Ruby. HtmlUnit is a Java library, and Celerity is just a Waitr API based wrapper of it. So it only works on JRuby. I thought the Gemfile would just handle that, but I was very very wrong. Therefore, you have to do something like this:



platforms :jruby do
gem "celerity", "0.8.2"
gem "syntax"
end

if RUBY_PLATFORM =~ /mswin|mingw/ #Jruby no likey mingw
platforms :mswin, :mingw do
gem "watir", "1.6.7"
gem "watir-webdriver"
gem "win32console"
gem "win32-clipboard"
end
end

This works out pretty well, just don't check in your Gemfile.lock. Ideally your QA testers will be running the Watir versions, while CI will maintain sanity when they can't. If you don't have QA testers, then I suggest having two versions of the project open, with separate Gemfile.lock that represent each platform.

I'm certain there's a better way to do this...but I've not discovered it yet.

Another random thing. If you want to pass a parameter (like a table) from one Cucumber step definition to another, you use a comma at the end of the line and pass the parameter in.

In my case, I was developing tests that triggered various UI based errors on each page during a process to say retrieve a password.

So I had a step definition that said:


When /^I forget my password and enter the credentials$/ do |table|
# table is a |username|account_number|
#blah blah blah with username and account_number
end

But what do I do when I have passed this step and need to enter security questions? I want to go through the successful parts of this step to enter the next step. How do I do this?


When /^I forget my password and enter the credentials and answer the security questions$/ do |table|
# table is a |username|account_number|answer1|answer2|answer3|
When 'I forget my password and enter the credentials', table
# Do stuff to answer the security questions

See that comma at the end of the step definition call? It passes table to the next step and lets it grab username and account_number out of the hash. I couldn't find this anywhere in documentation, which means it's painfully obvious to Ruby folks, but it's not if you don't know it. I'm sure it's documented somewhere, but I've failed at finding it. But boy discovering that was quite useful.


One last thing that I can think of right now. For some reason the version of JRuby I have to use doesn't like to use rspec. So similarly, to the first problem, we need to do strange things to make JRuby happy in env.rb:



require 'rubygems'
if RUBY_PLATFORM =~ /java/ #JRuby is strange
require 'spec'
else
require 'rspec'
end


Hopefully these tips will be of some use to folks like myself who don't know enough Ruby to be dangerous...yet.

Wednesday, August 18, 2010

Cleaning up after maven-eclipse-plugin

There are a few of us out there who are in situations where we cannot use the m2eclipse plugin to run our Maven based projects without pain. To resolve that pain there's the maven-eclipse-plugin.

Unfortunately, the current version of the plugin has a bug which causes an in issue where duplicate facet entries are placed in the org.eclipse.wst.common.project.facet.core.xml file.

As an example:


<faceted-project>
<fixed facet="jst.java"/>
<fixed facet="jst.utility"/>
<installed facet="jst.utility" version="1.0"/>
<installed facet="jst.java" version="5.0"/>
<installed facet="jst.java" version="5.0"/>
<installed facet="jst.utility" version="1.0"/>
</faceted-project>

How to solve this problem? A convient excuse to try out some Groovy and the GMaven plugin. Fortunately, Groovy is pretty good at modifying XML, and GMaven is pretty good at playing within the maven pom.

Here's the Groovy script:



if (project.packaging != 'pom') {
def facetXml = new File(project.basedir, '.settings/org.eclipse.wst.common.project.facet.core.xml')
def facets = new XmlParser().parse(facetXml)
def facetSet = [] as Set

log.info('Loaded Eclipse Facet XML')
facets.installed.each {
if (! facetSet.add(it.'@facet')) {
it.parent().remove(it)
log.info('Removed ' + it.'@facet')
} else {
log.info('Put in ' + it.'@facet')
}
}
log.info('backup old XML')
new File(project.basedir, '.settings/org.eclipse.wst.common.project.facet.core.xml.bkp').delete()
facetXml.renameTo(new File(project.basedir, '.settings/org.eclipse.wst.common.project.facet.core.xml.bkp'))
log.info('writing items')
def newFacetXml = new File(project.basedir, '.settings/org.eclipse.wst.common.project.facet.core.xml')

new XmlNodePrinter().print(facets)

PrintWriter pw = new PrintWriter(newFacetXml)
xmlNodePrinter = new XmlNodePrinter(pw)
xmlNodePrinter.setPreserveWhitespace(true)
xmlNodePrinter.print(facets)
pw.close()
}


I'm sure there's some code golf you could play to shorten this up, but it does the trick. When you run it, the two extra entries in the file are gone, and a harmless backup file is left behind.

Wednesday, June 02, 2010

Sabotaging Your IT Team, Straight From 1944.

Today I ran across a highly entertaining post on Volokh which links to a manual on sabotage used during World War II. Most of the stuff in the manual is standard stuff, fires, blowing up machinery, etc. However, the really entertaining stuff begins on page 28, which is the "corporate" section. It details how to cause corporations to drive to a halt.

There are many other blogs that could dissect and twist this into any number of ways. Most of them have noticed how government tends to follow all of the recommendations as standard operation procedure. But since this is a blog which has primarily focused on programming, I'll spin this towards Agile/Lean project methodologies and the old Waterfall standby. And some of this I'll just spin towards IT in general.

So open up the PDF and skip ahead to page 28 of the Simple Sabotage Field Manual (page 32 in the PDF) and lets enjoy the mayhem. Some of these are a bit harder to connect, but they're spinnable, and what's the fun without spin?

Step a-1: Insist on using "channels". No changes to your project to deliver what is actually needed when you notice it. No team authority. Push your request up, get the change orders done, then work like hell to make it happen.
Step a-2: Make speeches. You know the guy who rolls into a project tells you you're doing it all wrong, and your management listens and tells you to do it a different way? That's this step.
Step a-3: Refer matters to committee. Same as a-1.
Step a-4: Bring up irrelevant issues. Attempting to change work in the middle of a development iteration/sprint. Unless you've got a major production outage, the issue can wait until the end of your work cycle.
Step a-5: Haggle over precise wording. This is more of a problem when you cannot get good copy up front. It can delay your work in waterfall and agile, so this one is just bad all around.
Step a-6: Reopen closed issues. Reopening the design of the system without a major problem that you actually need to solve. Creating more work for works sake, not because there's debt to be paid off.
Step a-7: Advocating "caution". Extra concern raised due to the unknown. This is bad for a team since it reduced the confidence to achieve success.
Step a-8: Be worried about authority to make a decision. Being afraid to make a decision since you might not have authority. You should have authority. This is similar to a-1, a-3.
Step b-1: Demand Written Orders. Well, we all need specs, but in Agile/Lean, we're getting those as we need them. We don't require the entire system be specified.
Step b-2: Misunderstand Orders. Quibbling over specs. This seems to occur when the business is not involved and rolls in several months down the line and says this isn't what they needed. Eliminate using Show and Tells.
Step b-3: Don't deliver until completely ready. Big Bang approach to delivery of software. Yep, that's Waterfall.
Step b-4: Don't order new working stocks until completely gone. This is what is known as Kanban isn't it? Pull work into the queues. There is no pull, only the schedule.
Step b-5: Order high quality materials which are hard to get. I'll change this one. Order expensive software/hardware that require expensive maintainence contracts. Especially when there's a well maintained open source project that your company could fund 2 developers to work on full time at a much lower cost.
Step b-6: Don't prioritize properly. When the business does not work with the development team to prioritize features, then the wrong thing gets prioritized by one of the two parties involved.
Step b-7: Insist on being perfect. Misuse of metrics. Insisting on 100% code coverage, zero issues in your quality checks as though it will eliminate Technical Debt. It won't. It will just make you move slower and ignores fixing the most expensive debt.
Step b-8: Mistakes in routing. Sorry I got nothing here.
Step b-9: Give trainees misleading instructions. This is more of a problem of new programmers not given enough time to gel into the new team. Train them through pair programming.
Step b-10: Be pleasant to ineffective workers, give them promotions. Um, Dilbert cornered the market on this one.
Step b-11: Hold conferences when there's important work to be done. Eliminate meetings. Prime Agile/Lean principle.
Step b-12: Multiply paper work. Unnecessary documentation for sure, but also poor technical design which doesn't allow for code reuse.
Step b-13: Multiply procedures. Too many people required to approve change controls, code sign off. Increased bureaucracy. Agile, Lean, ITIL, even Six Sigma applied to software have reduced these issues. And more than likely, unless their a micromanaging jerk, the upper management doesn't want to be that involved.
Step b-14: Apply all regulations to the last letter. Ever have a simple problem in a production install. Say a test property file was installed instead of production and the entire project is backed out rather than let the team fix the problem on the fly? Yeah, me too.


There's more comedy gold in here, but since I like to blog by the seat of my pants without editing, I need to stop. Heck, I've got a 1pm meeting to attend that is, well...let's just say that a-1, a-4, a-5 are to blame for this b-11.

ShareThis