Pages

Saturday, January 28, 2012

Understanding the power of isolated object testing

I spent some time last night watching J.B. Rainsberger's excellent InfoQ presentation entitled "Integration Tests are a Scam". I've been recently contemplating why I favor unit testing (or as J.B. calls it, isolated object testing) over integration testing and I found his presentation particularly relevant. I highly recommend watching it. He also has a series of blog entries that support the presentation (Parts 1, 2, and 3). I also recommend reading those. They're truly gems.

 

Testing to prove basic correctness

I find many developers using integrated tests as a way to prove the basic correctness of the class or system under test. J.B. writes that "While integration tests offer value in other contexts, too many programmers use them to show basic correctness, and when they do that they waste a tremendous amount of time and effort." Integrated testing can be used within a project (I'm personally fond of acceptance testing), but integrated testing should not be used to prove basic correctness of your code. Focused, isolated object tests (aka unit tests using test doubles) should be used for this endeavor. If you discover behavior that a collaborator demonstrates and you have not accounted for in your isolated object tests, you should mimic this behavior in your test doubled collaborator contracts. You want to cover as much of your code with isolated object tests. We'll talk more about contract tests later.

J.B. mentions using an integrated test to learn about how a collaborator might support its contract, but that this integrated test is not included in the basic correctness test suite. I'm wondering if there isn't some other test suite here that we could use to keep integrated tests that support our learning the runtime and external dependencies. This test suite would be run periodically, but is not part of the whole continuous integration process of building a software system. Need to noodle on this more.

 

Isolated object testing

J.B. states in his aforementioned presentation that he does not use the term "unit testing" and instead favors a more focused term of "isolated object testing". He makes a point to call out the isolated word; these tests isolate the class under test by using test doubles to stub or mock the collaborators of the class under test. These tests focus on a single object and a singular behavior. Any collaborations are realized using test doubles.

I tend to agree that the phrase "unit testing" is a weak phrase describing the type of testing that we use to drive design of a single class and a single method. These tests focus on the responsibility of a single method of a single class. These tests also help you focus on understanding the contracts of collaboration between this class under test and its collaborators. This is really where the design part of the equation comes in. Classes that are hard to test are screaming out that the design is wrong. The tests are saying you have too much responsibility in the class under test or there are too many collaborators. Using your fast isolated object tests, you can engage in a reactive design activity, moving responsibilities out of the class under test, create new collaborators, and other design changes to make .

 

The collaboration contract

J.B. rightfully makes a big deal about contracts (aka interfaces) to explicitly specify collaboration protocols between objects. He mentions that in Domain-Driven Design, popularized by the Eric Evans book of the same name, three concepts can be used to express a domain-driven model: Values, Entities, and Services. All Services should have contracts and those contracts manifest themselves as interfaces. By specifying interfaces, we explicitly declare the protocol supported by each interface implementation. Constraining ourselves to interface types when specifying collaborations results in looser coupled systems, which is considered a "Good Thing". When engaging in isolated object testing, J.B. details the concept of collaboration tests and the closely associated contract tests. This concept of collaboration tests and contract tests is something new to me and is a big reason the presentation was so valuable in my eyes.

 

Collaboration tests and contract tests

After watching the presentation, I seem to be doing a pretty good job of using collaboration tests, but I'm not making the association to contract tests. Collaboration tests prove that the client interacts with its collaborators correctly; the client sends the correct messages and message arguments to the collaborator and appropriately handles all outputs from the collaborator. This is traditionally what I have used mock objects for and that seems to be what J.B. is arguing one should do for collaboration testing.

Contract testing, on the other hand, deals with testing that an interface implementation accurately respects the interface it is implementing. Does the implementor support the contract it declares to support? I haven't typically written these types of tests, but I'm going to start. Interestingly, I don't see a lot of this sort of testing in the wild. The part I really dig about contract tests as Rainsberger explains them is that they can be reused across interface implementations. He uses the List interface and two of its implementations, ArrayList and LinkedList, and details how to use implementation inheritance in the tests to DRY up your contract tests.

It really gets interesting when he declares that for every collaboration test that implies that a test double behavior, there better be a corresponding contract test that demonstrates that the interface implementation(s) actually does support that behavior. Same goes for values returned: if a test double returns a value in a collaboration test, there should be a contract test that demonstrates that the real implementation(s) does actually return that value. This is where I think selective, integrated learning tests can help you discover how your classes on the edges of a system may act when integrated to real external dependencies. But again, those integrated tests are not providing basic code correctness semantics. They're in your project to help you learn, but are not part of the isolated object tests suite. Don't lump them with your isolated tests and don't run them as part of your code/update repos/run tests/commit cadence.  This learning test suite should be run periodically during the day, but not part of the CI build process.

The death spiral of integrated testing for proving basic code correctness


Both Rainsberger's tutorial presentation and his blog postings go into great detail to the fallacy of using integrated testing to prove code correctness. I won't rehash what he has to say about it. My conclusions that I draw from his material are:

  • Focus on isolated object tests and isolation techniques and learn how to do collaboration and contract testing.
  • If you need to learn, by all means, write an integrated test. If you need to reproduce a defect, initially write it as an integrated test. However, in all cases, take what you have learned and replicate that in your isolated object test suite.  Replicate the behavior you discovered in the integration test(s) with test doubles.  This will ensure that your isolated object tests stay true to the behavior of the integrated system.
  • Don't get sucked into the downward death spiral of using integrated testing to guide your test-driven design efforts. It will only cause you pain.


J.B.'s series on Integrated Tests are a Scam can be found here.



 

Friday, November 18, 2011

Spring Security Grails plugin 1.2.4 and transparent password encoding in the User domain object

Just spent some significant time today integrating Spring Security Grails plugin 1.2.4. It should have been a quick integration but I got stuck on some new functionality that has been added to the generated User domain object that this plugin generates when using the quick start script. You should now set the password on this domain object using the cleartext string. Look at the User domain object (you may have it named something else, but it's the domain object that represents the user). This domain class is now handling the encoding of the password transparently. I copied some code from another Grails app that was doing the encoding of the password explicitly. You should no longer do this--it will cause problems when attempting to authenticate. Basically I was doubly encoding the password, the plugin definitely does not like that. Here's what the plugin generates for a user domain object in 1.2.4:

 1 class User {
2
3
transient springSecurityService
4
5
String username
6 String password
7 boolean enabled
8 boolean accountExpired
9 boolean accountLocked
10 boolean passwordExpired
11
12
static constraints = {
13 username blank: false, unique: true
14 password blank: false
15 }
16
17
static mapping = {
18 password column: '`password`'
19 }
20
21
Set<Role> getAuthorities() {
22 UserRole.findAllByUser(this).collect { it.role } as Set
23 }
24
25
def beforeInsert() {
26 encodePassword()
27 }
28
29
def beforeUpdate() {
30 if (isDirty('password')) {
31 encodePassword()
32 }
33 }
34
35
protected void encodePassword() {
36 password = springSecurityService.encodePassword(password)
37 }
38 }


Notice that the domain object now has a springSecurityService injected into it. There's also some GORM callbacks that will be called before the state of the domain object is saved and updated in the database. This is where the encoding now occurs--you should not be doing the encoding explicitly yourself.

Lesson learned!

Thursday, October 13, 2011

Grails GSPs consume Underscore.js ERB-style interpolation delimiters

If you are planning to do any work with Grails and Underscore.js (or Backbone.js) and you have view templates maintained in Groovy Server Pages (GSP) with Underscore.js ERB-style interpolation markup, watch out! GSP will eat up the Underscore.js interpolation markup (<%= %>) before Underscore ever gets a chance to use it. I assume evaluations would also be consumed by GSP. Took me a while to figure this out, so I thought I'd raise it up as a concern. Underscore.js does give you a way to change the delimiters using regexes.

Wednesday, October 12, 2011

Rationale for CoffeeScript's 'Fat Arrow' syntax

I gave a CoffeeScript presentation last night at our local Groovy Users of Minnesota meet up. One of the questions that came up last night had to do with CoffeeScript's fat arrow (=>) syntax, which will both define a new function and bind the new function to the current value of this. This is super helpful for callback-based libraries, such as jQuery.

I was building a Backbone.View today (in JavaScript) and had to deal with this binding. In my JavaScript, I wanted to call fadeOut on the view's el property (which happens to be a jQuery wrapped element). Without using the jQuery proxy method to bind this appropriately, my statement of 'this.el.fadeOut();' will not work. The this reference is no longer the view at the point I'm using it in the anonymous function; the context for this has changed within the anonymous function declaration. The correct JavaScript code is below.


var MyView = Backbone.View.extend({

initialize: function() {
this.template = _.template($('#my-template').html(), this.model.toJSON());
this.render();
},

render: function() {
this.el.html(this.template);
return this;
},

events: {
"click button#doSomethingButton": "doSomething",
},

doSomething: function(e) {
this.model.set({someValue: $('#someValueTextField').val()});
var promise = this.model.doSomethingOnModel();
promise.done($.proxy(function() {
this.el.fadeOut();
}, this)
).fail(function() {
alert('Failed to check sequence uniqueness.');
});
}
});


So how does this relate to CoffeeScript? Well, the fat arrow operator is performing the proxying of the 'this' reference for you. If I wrote the above in CoffeeScript, I could write the done callback as:



promise.done => @el.fadeOut()



Chaining the done and the fail callbacks would necessitate the use of parentheses, but still very succinct. Score one for CoffeeScript!

Wednesday, October 05, 2011

Using JDBC URLs containing LDAP URI to connect to Oracle databases within Grails

I'm working on a Grails application that needs to connect to a Oracle database using a LDAP context.  The URL format is something like the following:

jdbc:oracle:thin:@ldap://tns.mycompany.com:389/marketing,cn=OracleContext,dc=com,dc=marketing

I'm also not using the Grails DataSource.groovy configuration for this.  I'm managing a separate DataSource in the resources.groovy using Spring DSL.  I'm using the org.springframework.jdbc.datasource.DriverManagerDataSource.  I have not tried this with the standard DataSource.groovy stuff.  When I first tried using this, I would get an exception with the following text: "javax.naming.NotContextException Not an instance of DirContext".  There seems to be a bug with the Spring LDAP and the SimpleNamingContextBuilder class.  Basically the SimpleNamingContextBuilder returns a Context implementation, not a DirContext implementation.  You can work around this in Grails by adding the following to the Config.groovy file:

grails.naming.entries = null

Problem solved.  The DataSource now bootstraps correctly and I can go on my merry way.  Kudos to Luke Daley for bringing this to my attention.

 

Monday, October 03, 2011

Hiking around in Big Woods State Parks

Some photos from our family hike through Big Woods State Park near Nerstrand, MN. Beautiful fall colors. Not quite peak in this area, but we'll take it. Awesome weather.

Big Woods State Park

Wednesday, September 07, 2011

CoffeeScript presentations

I did the first CoffeeScript presentation this past August to the Ruby Users of Minnesota (RUM) group and it looks like I'll be doing a second take on the presentation to the Groovy Users of Minnesota (GUM) here in October.  If you're interested in the presentation and the examples, you can find them here.

Tuesday, September 06, 2011

Tracing and profiling SQL in Grails using log4jdbc

I spent some time today tracing and profiling SQL in one of the Grails applications that I support.  I was looking around for proxy JDBC driver and happened on log4jdbc.  It's similar to p6spy, but it seems to be actively developed and supported.  Downloaded the driver, dropped it in my lib directory, and changed the logging and datasource configurations a bit in Grails and I was up and running.  Very handy.  I made copious use of the SQL timings profiling today.  There are many other options for tracing and profile with this tool.  Here are my changes to Config.groovy for enabling SQL timings to all SQL statements:

Config.groovy change to enable logging of SQL information from log4jdbc:

log4j = {
info 'org.codehaus.groovy.grails.web.servlet',

...
'grails.app',
'jdbc.sqltiming'
}

 

DataSource.groovy changes to enable log4jdbc:

development {       
dataSource {
driverClassName = "net.sf.log4jdbc.DriverSpy"
url = "jdbc:log4jdbc:mysql://localhost/mydb-DEV?useUnicode=true&characterEncoding=UTF-8&useCursorFetch=true&autoReconnect=true"
}   
}

 

Can't say enough good things about this tool.  Really helped me zero in on some queries that were performing poorly with large data sets.

Wednesday, August 24, 2011

Identifying producer/consumer scenarios in the wild

I've been working on a data import process the past couple of days, trying to solve some memory issues (OOMEs).  Essential we have a reader (the producer) and a writer (the consumer).  The writer part of this scenario operates much slower than the reader part.  The reader part is implemented as an iterator, so it only produces enough work for the writer to consume.  As this design evolved over time, parallel execution of the writer was added, in an effort to speed up the overall writing process.  The coordination of the parallelization is an ExecutorService implementation.  With this executor service now in place, the iteration of the reader can operate independently of the writer.  Thus, the consumer now starts creating lots of tasks that are then submitted to the executor service, where they queue up.  The executor service is not bounded, so it just keeps accepting tasks.  This wouldn't be a problem if the number of tasks were small and the memory footprint of those tasks was low, but that is not our situation.  Thus, we keep blowing out our Java VM process with OOMEs.  We're in the process of fixing this issue, using a bounded concurrent collection to handle the buffering of items between the reader and the executor service and ultimately the writer.

 

In support of Groovy's dynamic method dispatch

Came across a design issue today where we really could have used Groovy's dynamic dispatch and multi-method support to invoke the proper method based on the interrogation of the runtime type of an object instance passed as a parameter to the method.  This blog posting by MrHaki gives a great description of how Groovy solves this problem without the need of a double dispatch pattern implementation.  We're writing our code in Java, so we don't get the Groovy goodness of dynamic method dispatch and multi-methods.  We resorted to a double dispatch implementation to solve our problem.  We did not use the instanceof operator and a whole bunch of conditionals.

Sunday, August 07, 2011

Book review: Clean Coder

Clean Coder, The: A Code of Conduct for Professional ProgrammersClean Coder, The: A Code of Conduct for Professional Programmers by Robert C. Martin

My rating: 3 of 5 stars


Decent follow-up to Clean Code. Much of the book has been talked about in other avenues. The professional attitude discussion was decent, but mostly common sense. I liked the book, but have seen the content before. Nothing really new here. Definitely meant more for new developers and would wholeheartedly recommend it for that group of people.



View all my reviews

Book review: The Great Derangement

The Great Derangement: A Terrifying True Story of War, Politics, and Religion at the Twilight of the American EmpireThe Great Derangement: A Terrifying True Story of War, Politics, and Religion at the Twilight of the American Empire by Matt Taibbi

My rating: 3 of 5 stars


Not nearly as good at Griftopia, also by Taibbi. Kind of wanders and there doesn't seem to be any real plot to this book. Kind of wonder if Matt was still on drugs when he wrote this thing. The religion parts were semi-interesting, the political insights were boring. Can't recommend this. Some good parts, but the whole doesn't make for a good read.



View all my reviews

Monday, July 25, 2011

Controlling transitive dependency resolution in Gradle

Just hit this so I thought I would write up a quick entry.  I'm trying to get Hibernate and Apache CXF to work together.  I have a Gradle build.  I ran my test suite and I am seeing issues with CGLib classes.  After a little bit of research, it seems there's an issue between the ASM library that Hibernate's CGLib uses and the one that Apache CXF uses.  Solution is to exclude cglib-2.1_3.jar and use cglib-nodep-2.1_3.jar instead.  To do this in Gradle:

configurations {
   all*.exclude group: 'cglib', module: 'cglib'
   ...
}

dependencies {
   compile group: 'cglib', name: 'cglib-nodep', version: '2.1_3'
   ...
}

Adding these lines to the build.gradle file allow me to remove the cglib-2.1_3.jar dependency and instead specify the nodep version instead.  Pretty slick.

 

 

Thursday, July 14, 2011

Sharing a 27" Cinema Display using an Atlona AT-MDP21 KVM switch

Quick blog post here.  Hooked up an Atlona AT-MDP21 2x1 Mini DisplayPort KVM switch to my 2009 Mac Pro and my 2010 MacBook Air.  Both run the 27" Cinema Display at its highest native resolution, 2560 x 1440.  Atlona documentation states highest resolution is 1920 x 1200.  Easy to set up and I have the Cinema Display USB in the back working between the two computers.  The only downside is the slight 1-2 second pause when switch the KVM from one computer to another.  Not a big deal.  Highly recommended.

Friday, June 24, 2011

Kingpin: How One Hacker Took Over the Billion-Dollar Cybercrime UndergroundKingpin: How One Hacker Took Over the Billion-Dollar Cybercrime Underground by Kevin Poulsen

My rating: 5 of 5 stars


Awesome non-fiction. Author does a great job keeping the drama and action going. I couldn't put this book down after I started reading it. Interesting to watch Max Butler's (aka Max Vision) downward spiral into cybercrime and black hat hacking. Very eye opening to read the details of how these talented hackers can cloak their infiltration and syphon information from computers for weeks or months.



View all my reviews

Friday, June 17, 2011

Zero Day by Mark Russinovich

Zero DayZero Day by Mark Russinovich

My rating: 4 of 5 stars


Pretty good cyber-thriller. I think this is Marks' first novel. Knowledgeable author. The plot is very plausible and believable. A fair amount of technical information around malware, rootkits, computer viruses. A fun read. Very quick read and I found myself not wanting to put it down, especially after I got to the second half of the book. The last third of the book is like a runaway freight train of suspense. Recommended!



View all my reviews

Sunday, March 27, 2011

Grails JAR dependencies with classifiers

Quick post on specifying Grails dependencies in BuildConfig.groovy.  The recommended way to suck in JAR dependencies in Grails is to use the dependencies DSL maintained in BuildConfig.groovy.  I had a need to bring down a dependency that has a classifier attribute on it.  Didn't really find anything definitive on how to do it, but it seemed like following a convention might do the trick.  Here's how I solved the issue:

repositories {
  grailsPlugins()
  grailsHome()
  grailsCentral()
  mavenCentral()
  ebr() // SpringSource Enterprise Bundle Repository
}
dependencies {
  runtime group:'net.sf.json-lib', name:'json-lib', version:'2.4', classifier:'jdk15'
}

Saturday, March 05, 2011

Griftopia by Matt Taibbi

I recently read this book after seeing Alan Cooper had read it and stated that it was a terrifying book.  I wondered what would be so terrifying about "Bubble Machines, Vampire Squids, and the Long Con That is Breaking America".  After reading it, I wouldn't characterize it as terrifying as much as I would characterize it as infuriating.  The incompetence, greed, self-interest, and gluttony that is repeatedly portrayed in the book is extremely infuriating to me as a hardworking American citizen that pays taxes.  The book chronicles some of the most audacious power grabs this nation has ever seen, and in most instances, those power grabs are happening during the past two decades.  Taibbi chronicles why the Tea Party is chasing its own tail, lambasts Alan Greenspan as "a one-in-a-billion asshole that has made America the mess it is today", and details the mortgage, commodities, and wealth fund scams that we, American taxpayers, have had to endure the last couple of years.  The book is written in a no-holds barred fashion with a fair amount of profanity thrown in to spice up the prose. It's an entertaining read, but also very thought provoking and sheds some interesting light on the current political climate, especially around Obamacare and the health insurance industry.  Very highly recommended.

griftopia-e1296531461300.jpg

 

 

Friday, March 04, 2011

Groovy Remote Control plugin via Maven

I had some issues getting the Groovy Remote Control plugin to pull down through Maven today.  The documentation that is currently in place today is not correct.  Here is the fragments of my Maven POM that enabled me to pull the plugin as a dependency:


 

 

Sunday, February 27, 2011

It's all about the conversations!

More pondering as I contemplate my previous consulting gig.  This time, I'm considering requirements discovery.  Pre-agile, people would write large requirements documents, hoping to document all the requirements needed for the developers to build a system that would satisfy the customers.  Unfortunately, this view of discovering all the requirements ahead of time is awfully naive.  Things change.  Requirements go undiscovered.   Requirements that are captured are not thought out as well as we would like and when it comes time to implement the requirements in software, the requirements don't make sense or are plain wrong.  Thus, large efforts to capture all the requirements has some amount of wasted effort.  This effort focuses most of the conversations at the beginning of the project.  Conversations after the requirements document has been written and signed off is discouraged; it's viewed as evidence that there are errors in the requirements document.  There doesn't seem to be any room for learning with requirements documents.

Now we have agile methods and the user story.  User stories are not requirements.  They're a planning tool for the agile team.  They are a statement of value that the business would like built.  There may or may not be acceptance criteria associated with the user story, depending on where the user story is in its lifecycle.  The most important part of the user story is the conversations that need to happen to flesh the story out so the feature value can be realized in the product.  This detail seems to escape a lot of people trying to use user stories to build products.  User stories give me the freedom to have conversations with all interested parties regarding the statement of value.  I learn just-in-time about the requirements as I implement the feature.  The business learns about how those requirements they communicated manifest themselves in a product.  And we both have the freedom to adjust, learning about the product along the way.

The above statements about user stories does not preclude one from using prepared, written documentation to feed the conversation.  My previous gig, we have a lot of federal government documentation regarding reporting and calculation requirements.  These requirements are set in stone by the government.  Yet, there needs to be conversations as to how those requirements will be accomplished across releases and sprints.  This is where the conversations come back to the forefront.

User stories allow me to deliver value to the business or customer in bite-size portions.  Value is delivered in small increments, thus I can deliver these more frequently and solicit feedback from the business or customer.  When I have short feedback loops in place, I can nimbly change my course if I need to.