Friday, December 31, 2010

Rails4 - Iteration B2 Product controller tests fail.

The book solution demos adding an @update product not conflicting with the default fixtures :one and :two, which by default have the same title. (after adding uniqueness test on title, these fail).

It also works to fix the test data, just update the products.yml file and provide a unique title for :one and :two

one:
title: MyString One
description: MyText
image_url: MyString
price: 9.99

two:
title: MyString Two
description: MyText
image_url: MyString
price: 9.99

This allows the update test to pass no problem.

Note: you do still need to provide a unique title in should create product (where using :one as a template). Simple as adding one line into the method:
#after validation added, now required to provide a unique title.
@product.title="a unique test title"
#this is the part that takes the form input..
post :create, :product => @product.attributes

Thursday, December 30, 2010

Rails4: Iteration B2 - generic def for new default valid products

Building on example for "def new_product(image_url)" pg. 101

#a generic method to build new test produce with valid data
#but also allow insertion of any attributes that need to be over-ridden
#takes a hash of attribute values, checks for each and populates default as needed.
#don't add this to real Product, 'cause production code shouldn't provide defaults.
def new_valid_product(data)
Product.new(
:title => data.has_key?(:title) ? data[:title] : "A Book Title",
:description => data.has_key?(:description) ? data[:description] : "ipso lorum... a default description",
:price => data.has_key?(:price) ? data[:price] : 123.45,
:image_url => data.has_key?(:image_url) ? data[:image_url] : "test.gif"
)
end

#a quick test to warn me if any rules change that invalidate the default values.
test "default product attributes are valid" do
product = new_valid_product({})
assert !product.errors[:title].any?, "default title in error"
assert !product.errors[:description].any?, "default_description in error"
assert !product.errors[:price].any?, "default_price in error"
assert !product.errors[:image_url].any?, "default image_url in error"
end

#a quick test to prove to myself that the method accepts input data & uses it correctly.
test "default product attributes can be changed" do
product = new_valid_product({:title => "T", :description => "D", :price => 1, :image_url => "I"})
assert_equal "T", product.title
assert_equal "D", product.description
assert_equal 1, product.price
assert_equal "I", product.image_url
end

Note: My guess it that this kind of method is not really useful, Ruby has fixtures, my suspicion is that in time I'll find it much easier to define and get a fixture, then change a value or two, rather than write this kind of utility method for each model unit test class.

Tuesday, December 28, 2010

Rails4 - curl to download directory content

thanks to: http://www.linuxquestions.org/questions/programming-9/using-curl-to-dl-files-from-http-sites-with-wildcard-379067/ for the initial curl script line and to http://www.linuxquestions.org/questions/programming-9/extract-substring-using-sed-and-regular-expressions-regexp-702074/ for help extracting the filename from html.

for file in `curl http://media.pragprog.com/titles/rails4/code/depot_b/public/images/ | perl -wlne 'print $1 if
/href="(.*[gif|png|jpg])">/'`;do curl -o ${file} http://media.pragprog.com/titles/rails4/code/depot_b/public/images/${file};done

*Note: not happy with having to specify the file types, but the page HTML contains other links like Download, Parent etc so href=.* litters the results with junk files.


*Note would have been easier with wget, but for some reason darwin-ports for leopard DMG was unavailable.
should I get darwin-ports installed try this instead.

run from depot/public/images directory
wget -rkp -np -nH --cut-dirs=1 http://media.pragprog.com/titles/rails4/code/depot_b/public/images

@see http://psung.blogspot.com/2008/06/using-wget-or-curl-to-download-web.html

Thursday, April 29, 2010

Moving forward with customized keyboards in iphone 3.2

I recently inherited a project that had customized iphone keyboards by adding a toolbar following the techniques in http://www.iphonedevsdk.com/forum/iphone-sdk-tutorials/7350-adding-subviews-custimize-keyboard.html.

Sadly, this code behaves badly in the new SDK 3.2, it crashes. First time because of a index out of bounds, and once that was fixed then an infinite loop on keyboardDidShow. (And yes I did catch the object name change too). Luckily, 3.2 provides a new handy method to simply add the inputAccessoryView with out all the previous fuss.

But, do you want to release just a 3.2 app? If your in my position, the business sponsors do not want to hear that their hard won customized toolbar will only be seen on the iPad for a while. They expect backwards compatibility.

I solved this dilemma by using the UIInputTextField instancesRespondTo inputAccessoryView to guard the call that sets up the keyboard event listeners required in the older solution, so that only on 3.1 devices will the keyboard intercepts be executed. Then I setup a new message type AddToolbar to be called by TextFieldDelegates for fields where the toolbars will be needed.

Note that Apple does not 'publish' the Notification that triggers shouldTextBeginEditing or textFieldDidBeginEditing so instead of just having a global delegate I instead trigger this new AddToolbar notification as part of my TextFieldDelegate response.

The new message listener again checks for inputAccessoryView support and in 3.2 code makes the call to add the new Toolbar.

Saturday, April 24, 2010

A Trick for EasyMock with Legacy Code...

... where "Legacy Code" is, to paraphrase Michael C. Feathers, any code that's been in production longer than 3 months. Or in my case a new project coded over a year ago, by consultants, on a tight schedule. Lots of classes had been developed without consideration for unit testing which means lots of inter-dependent code without any hooks for dependency injection.

For example, there is a standard Config utility class that loads a property file and then provides getter methods for the values.

  • Hurdle #1: The Utility class only expects a ServletContext as a means to getting the file name to load.

  • Hurdle #2: Various path manipulations have been hard-coded in the file loading method as well.

  • Hurdle #3: The internal properties object holding the values is private accessor, designed to be read only by client classes.

  • Hurdle #4: Set up for ease of use with static method fields, so I can't just create an instance of my own or subclass easily.



In this project's case, I agree that the access to the config needs to be read only, as only bugs and mischief would result if later generations of developers thought that public or protected access to the Properties meant the config was expected to change at run-time. I also like the static behavior as it makes for clean code with no extraneous getInstance() or setConfig() calls everywhere the class is used.

On the other hand, I am simply too lazy to enjoy having to 1) set up my local environment to exactly match server paths. 2) build and deploy my code to test every code change and 3) stop and start the server to test every property permutation.

So, the challenge with this legacy code was to figure out how to inject properties at Test time without any impact on the existing code. Thankfully in Java I have Reflection and EasyMock.

First - a snapshot of the config class:
public class AppConfig {
 private static Properties applicationProperties;
 public static void loadConfig(ServletContext context){
  ... load files into appProperites
   //don't worry about this being static on
   //a constructed object it has been working over a year now..
 }
 public static String getProperty(String propKey) {
  return appProperties.getProperty(propKey);
  }
}

However, good isolated unit tests require I bypass all dependencies on file loading etc. so the first trick is abusing reflection to give me a point to inject a config mock object. Then I can create an EasyMock wrapper on the Properties object to inject which ever specific property and value my client code will need.

// written as a junit method for simplicity here
import org.easyMock.classextension.*;

public void testSomeObject() {
 AppConfig env = new AppConfig();
 Class c = env.getClass();
 try {
  Field appProps = c.getDeclaredField("applicationProperties");
  appProps.setAccessible(true);
  Properties overRideProps = EasyMock.createMock(Properties.class);
  EasyMock.expect(overRideProperties.getProperty("testkey")).andReturn("false"); //sets up our test property
  EasyMock.replay(overRideProps); // readies mock object to act during test.
  appProps.set(env, overRideProps); //puts our mock back into AppConfig object
 } catch (SecurityException e1) {
  fail ("denied access to reset applicationProperties to public " + e.toString());
 } catch (NoSuchFieldException e2) {
  fail ("applicationProperties field name changed " + e.toString());
 } catch (IllegalAccessException e3) {
  fail ( "Unable to override properties in AppConfig " + e.toString());
 } catch (IllegalArgumentException e4) {
  fail ( "Unable to override properties in AppConfig " + e.toString());
  //Note that you might see this exception if you import wrong form of EasyMock must use classextension
}

SomeObject so = new SomeObject();
SomeState testState = new SomeState();
... setup testState ...
String testResult = so.doSomeMethod(testState);
// If the above method does not call the expected property test will fail with EasyMock exception.
// otherwise it will fail if the following assertion fails.
assertEquals( "expectedResult", testResult);
}

Finally, a few words of caution on this style of testing

This test is brittle, it has knowledge of internal code working that would be better it not have. Now neither the Properties field name in AppConfig, nor the property key String can change without breaking this code. So there is a risk that a future developer will curse my name for requiring him to fix test cases for no good reason. If this were not Stable Legacy Code I would be more concerned about this risk.

This test case doesn't guarantee the system will behave as expected once deployed. There are no protections against keys changing in the property file used to load the AppConfig causing runtime defects. So some form of integration testing is needed to ensure the overall system works as intended later.

EasyMock may seem like overkill here, as I could just as easily injected a plain old Properties object. The benefit of EasyMock is that I can see exactly where the test fails and why - wrong key is used or no check is made. For all other reasons the final assert may fail I've got to go in and spend time debugging.

Thursday, February 4, 2010

Forget Part 1 - done right Groovy don't need a part 2

I had put this project on the shelf for time. New solution is half the code, requires only the one script file, and does even more.
Groovy's so flexible I can see where this could be condensed further, but then I'd have to double the comments to remember what it's doing next time I go to modify it.

def cruiseServer = 'http://build.server.com:9000'
def uploaddir = "user@123.123.123.123:/path/to/deployment/dir"
def downloaddir = 'D:\\localbuild\\downloaddir\\'

def rssPath = '/cruisebuild/rss/'
def artifactPath = '/cruisecontrol/artifacts'

//The common list of assets to download. With this list structure I can just comment out
// any asset to skip over each release or do the full download. Also can control if I want latest
// and greatest or a specific tested version.
def projects = [
//['comment out', 'assets not needed', 'latest'],
['Build-Project1', 'assetname.ear', 'latest'],
['Build-Project2', 'assestname.war', '12.12.1002']
]
//for the identified collection, do the download
projects.each() { project, ear, version ->
//get rss feed into a navigable object
def rssFeed = new XmlParser().parse( cruiseServer + rssPath + project).channel[0]
//get either the latest good build OR a specific version number
def passeditem
if ('latest'.equals(version) {
passeditem = rssFeed.item.find{ it.description.text() == 'Build passed' }
} else {
passeditem = rssFeed.item.find{ it.link.text() =~ version )
}
//link in RSS feed goes to the cruisebuild details, but what I need is asset download path.
def link = passeditem.link.text()
//reprocess the build number from link based on regex pattern for 4 sets digits w/ dot separator.
def buildMatcher = ( link =~ /\d+[.]\d+[.]\d+[.]\d+/ )
def buildNo = buildMatcher[0]
//Then the crucial info is the cruisebuild ID 14 digit number
def matcher = ( link =~ /\d{14}/)
def assetNo = matcher[0]
//so I can publish which ears were deployed
println "${project}.ear (V-${buildNo})"
//Now for the cool automatic download
def srcLoc = "${cruiseServer}${artifactPath}${project}/${assetNo}/${ear}"
def file = new File(dwnldDir + srcLoc.tokenize("/")[-1])
def out = new BufferedOutputStream(new FileOutputStream(file))
out <<>
//publish file size to confirm end of download
println file.length()
out.close()
} // end of download closure

//ah the miracle of AntBuilder - my files loaded straight to server after download
ant = new AntBuilder()
//note: in eclipse scp requires some path configuration.
//google the error you get when this runs to find correct jars for your version.
ant.scp( todir: "${uploadDir}",
password: "opensesme",
trust: "true" {
fileset(dir:"$dwnldDir") {
//this little closure ensures I only upload new assets.
//pretty cool to be able to mix builder syntax and script code.
projects.each() { project, ear, version ->
include(name: "$ear")
} //end projects.each closure
} // end fileset closure
} //end ant.scp closure