patkua@work

The intersection of technology and leadership

Page 40 of 53

Running tests on a specific OS under JUnit4

Our current development team is split, some working on windows machines, others on macs, with continuous integration tests running against both windows and linux environments. We’ve had a need to run operating specific JUnit4 tests and I got a little tired putting guard clauses into different @Before, @After and @Test methods to prevent a particular block of code running.

Using the new org.junit.runner.Runner annotation, and SystemUtils from commons-lang, here is the result:

package com.thekua.java.junit4.examples;

import org.apache.commons.lang.SystemUtils;
import org.junit.internal.runners.InitializationError;
import org.junit.internal.runners.JUnit4ClassRunner;
import org.junit.runner.notification.RunNotifier;

public class RunOnlyOnWindows extends JUnit4ClassRunner {

    public RunOnlyOnWindows(Class klass) throws InitializationError {
        super(klass);
    }

    @Override
    public void run(RunNotifier notifier) {
        if (SystemUtils.IS_OS_WINDOWS) {
            super.run(notifier);            
        }
    }
}

Our test classes, then look like this:

...
@RunWith(value=RunOnlyOnWindows.class)
public class WindowsOnlySpecificFooBarUnitTest {
    ...
}

Of course, you can use any other particular variations like SystemUtils.IS_OS_UNIX, or SystemUtils.IS_OS_MAC but we haven’t needed to yet.

Of course, this is easily turned into some sort of conditional runner abstract class but at least you get the basic idea.

java.io.File setReadonly and canWrite broken on Windows

File.setReadonly(true) doesn’t actually work on windows (at least on the JDK that we’re working on). According to the bug report filed here, it is just setting the DOS flag that prevents it from being deleted just not being written to.

Meanwhile, assuming you have mounted a read only disk partition in windows to, say X:, new File(“X:/”).canWrite() returns true when it really should be returning false. I’m still trying to find the bug reported to sun for this, or will later update this entry to include it. It seems to persist when running against both jdk1.5.0_15 and jdk1.6.0_07.

VMWare Disk Minimum File Size

The current versions of both VMWare Fusion and VMWare Player limit the minimum capacity of virtual disks (*.vmdk) to 100MB (or 0.1GB). Even their command line utility for windows (vmware-vdiskmanager.exe) describes this limit despite the file format allowing other file sizes to be specified (see screenshot below)

VMWare File Minimum Size

If you want to create a smaller disk, then I suggest downloading the QEMU program that allows you to create VMWare compatible disk images. When you install QEMU (at least for windows), you should find a qemu-img.exe file in their folder that gives you the ability to create your disk image. Here’s the command we used to create a 1MB VMware compatible image.

qemu-img create -f vmdk smallfile.vmdk 1M

Executing native processes in Java on Windows

This is probably something that old timers in Java will probably know, so I’m posting this more for my reference. Trying to execute:

Runtime.getRuntime().exec("dir");

results in the following stack trace.

java.io.IOException: CreateProcess: dir error=2
  at java.lang.ProcessImpl.create(Native Method)
  at java.lang.ProcessImpl.(ProcessImpl.java:81)
  at java.lang.ProcessImpl.start(ProcessImpl.java:30)
  at java.lang.ProcessBuilder.start(ProcessBuilder.java:451)
  at java.lang.Runtime.exec(Runtime.java:591)
  at java.lang.Runtime.exec(Runtime.java:429)
  at java.lang.Runtime.exec(Runtime.java:326)

After doing some reading

error=2

apparently means file not found. The fix for something like this is to first pass everything to the windows command line shell (cmd.exe on windows xp). This seems to do the job better:

Runtime.getRuntime().exec("cmd /c dir");

The slash-C means “Carries out the command specified by string and then terminates”.

Starting a new development project

I’m a big believer that if software projects aren’t set up correctly at the beginning, it potentially puts a lot of drag on the team going forward. This doesn’t mean planning everything meticulously before implementing it. Rather, its mean experimenting and evaluating whether the tools you choose give you the things you need for your project. When I set up new development projects, here’s some guidelines I try to follow:

  • Check out and go – Contributors should not need to install 10 applications, four plugins and configure special settings in order to start developing. I’ve successfully tried techniques like checking in your build tool (like ant) alongside your code, with windows and unix executables (go.bat, go.sh) that enable people to get consistent feedback quickly. On one project, we had all of the tools checked into a different subversion module and we ran a configuration script that set up the environment variables we needed to start developing. Eclipse allows you to bundle up all your plugins you need. If you don’t want to check things into source control, put it onto a standard share accessible to everyone. If you do need to install specific applications, list the details so they can run through it like a checklist (although prefer this as a last option).
  • IDE (un)bound – If you need to open your IDE to assemble your final executable, you’re far away from the ideal situation. It’s important for me that you can build your application in both your IDE and from the command line. Doing this early in the project helps raise issues about what can and can’t be done with your toolset.
  • IDE friendly – Having said that, it’s important that developers can debug easily, and so ensure that your code works with the environment and tools of your choice
  • Determine the testing strategy – Work with your QA to understand what things you will test, and to automate or try to automate. Determine what tools they might need to properly test your application.
  • Great example test cases – Trying to automate unit and acceptance tests is important to try before you have your final application (as the design may change to accommodate better testing). Your scripts should look neat and concise. If you have 100 lines of setup, 100 lines of test, imagine if you then need to write 1000 more of these and the nightmare your code will be to maintain.
  • Have a consistent code style – Strive for collective ownership. Having code that looks completely different from each other will detract from this. Define code format style early (where curly braces go, etc). Spend more time discussing the more interesting standards that may actually have an impact on your project (when, where and what to log, how to handle exceptions).

Keeping regular expressions readable

It’s easy for regular expressions to become one long string, involving lots of (round/square) brackets, backslashes, and other random symbols. It’s concise, yet often at cost of readability. We’ve recently been using a pattern that works well for us, where we’ve broken the expression down into its important constituents, still defined as a single string, but with a brief explanation for each part. Here’s a simple example:

Instead of public static final String SPECIAL_PATTERN = "(\\w)+-([a-zA-Z0-9])+-([a-zA-Z])+/\\w+" we modify the declaration slightly so it’s

public static final String SPECIAL_PATTERN
  = "(\\w)+" // at least 1 letter, number or underscore
  + "-([a-zA-Z])+" // dash with at least one number or letter 
  + "-([a-zA-Z])+" // dash with at least one number or letter 
  + "-(\\w)+" // dash with at least one letter, number or underscore

In our situation, applying Extract Constant or Extract Variable I think would have reduced readability so is a nice tradeoff of conciseness with readability.

Book Review: Agile Adoption Patterns

Agile Adoption PatternsI will confess at the outset of this review that I did not buy the Agile Adoption Patterns book, rather it was sent to me by the author (Amr Elssamadisy) whom I’d met a few years ago at XP2006. I intended for my review to be as independent as possible but wanted any readers to recognise the potential for bias given the context. The history of the book is an interesting one, combining the author’s own experiences with that of many agile practitioners derived from workshops that he ran at many agile/xp conferences over the course of several years. The benefits for the reader is that it captures the lessons learned of a collective crowd from a number of different contexts, something difficult for a single person to achieve on their own. One side effect of this is that the practices are a little self selecting with many of the practices biased towards XP and Scrum practices (and less of those originating from FDD, DSDM or lean).

I ended up reading this book twice before writing this, although the first time I simply flipped through it lightly to get a feel for the book. I remember distinctly thinking, ‘Yet another book on agile practices? Do I really want to spend more time reading through it?’ The second time, with much more time on a train, I’m glad I did as the author spends a lot of effort setting the context for how to go about using the book.

Given the content, I do feel the title of the book slightly misleading as it talks less about patterns in which people successfully (or unsuccessfully) adopted agile as a whole. Though a good book at what it does, I think it would have been better called, “A framework for adopting agile practices” or “Patterns of agile practice adoption” given its emphasis on the practices and less so on the principles behind them. For me, I find it’s important people recognise the difference between practices (good for beginners during the Shu learning phase) and principles and values (better for the Ha and Ri learning phases) and I’m a little disappointed that little of this seemed emphasised. Of course, this book is not targeted at seasoned agile practitioners, so it fits the audience. I think it bothers me that people thinking that adopting practices alone automatically makes them agile especially when you consider sustainable agile in organisations.

Structurally, the author lays the book out well, setting the context and history of the book and the intended audience. Although he mentions it’s not useful for advanced practitioners, I do think there is value as a means of diagnosing smells in practices for teams that already implement the individual practices. To get the most of out, I would recommend they simply jump to the practices they’re using to see if they recognise any signs of the smells described.

I like the format and I do believe this book contains a lot of useful information although suffers from a small set of problems. One of them is that it suffers the same problem many other pattern books do – it’s a great reference over time, yet a little too repetitive to read in one sitting. Another is that I think this is a great reference for people starting, yet leaves a gap about what to do when you get some of the practices implemented. Often I find some practices are great intermediate practices (iterations and time boxing as a way of developing rhythm) yet doesn’t detail what people should do to ensure agile is sustainable in the long term (dropping practices, and developing their own). My only other issue, as minor as it feels, is that the aesthetics of the book feel really dated, with many of the diagrams and the cover feeling like a textbook I would have used for university.

On the plus side, the author presents a simple, clear and step framework for people to follow when looking at a wealth of agile practices. It’s not prescriptive, and gives a balanced perspective by describing the benefits and the possible less desirable side effects of an individual practice and highlighting the impact of wrongly applying a practice in the wrong context. This is a great addition to the large body of literature on agile that will prove useful for early adopters.

Treat your App.config like a global variable

I remember looking at a few codebases in C# and it surprised me how many people encapsulate some of their system well, yet treat their application configuration as a different thing. Many times I’ve seen code that looks like: ConfigurationSettings.AppSettings["DatabaseConnectionString"]; alongside the thing that is using it. In fact, I often see this code repeated in various parts of the system wherever anyone needs to draw on application configuration.

Global variables are bad
Most good programmers seem to realise that most global variables are bad, yet this is exactly what I think accessing the application configuration is like. If you want to change the way your configuration is loaded (i.e. programmatically or from a database) think of how many places you now need to change it, let alone how it effects the order in which you instantiate objects.

My advice: Separate the use of configuration from where it declared
I try to follow the pattern isolating the configuration into a single class depending on the access method, such as:

  • DatabaseBackedConfiguration
  • FileBackedConfiguration
  • DefaultInMemoryBackedConfiguration

Each of these classes them implement several roles depending on what configuration they might provide such as:

  • HibernateConfiguration
  • ApplicationConfiguration
  • ArchivingConfiguration

Having smaller roles makes it clearer what sort of configuration is really needed for a particular consumer, and it allows me to change the decision about how that configuration is now provided to it. It enables me to do things like specify different environment configurations and do so in the method that I prefer.

Defensive programming depends on context

I remember stepping into a heated discussion with two developers who just couldn’t agree whether or not they should be writing their code defensively. One of them argued they should always be checking for null, checking for illegal input and throw appropriate exceptions before doing anything with it. The other argued that they should not because of the amount of code that would add to every single method. I think I understood where they were both coming from and suggested that they were both correct though under different circumstances. One of them asked me to explain, and considering I see this argument resurfacing again here and here, I thought I’d post my thoughts.

Defensive Programming

Scenario A: Dealing with those outside of your circle of trust
Let’s say you’re working in Team A, developing an API for the greater public. It might be as big as a core platform development API, or an opensource library. Either way, it’s not possible to learn how everyone is going to use your API and not enough to simply document it because some people will not read all the documentation. Programming defensively helps consumers understand how your code was meant to called because there is no single way you can ensure your clients have the same understanding, regardless of the amount of documentation you put together. They lie outside of your sphere of influence. The longer the release cycles, the greater the risk someone might use your API in a way you hadn’t intended. This is the situation the authors of the .Net Framework Guideline worked under and as such you’ll see they’re emphasis on defensive programming. You don’t always have to do it in this situation, you’re just more likely to.

Scenario B: Dealing with those inside your sphere of influence
You might be working closely with another team, either in your organisation or in another one, writing an API for them to consume. Do you write your code defensively? I think it depends on how much influence you have. If you have enough influence that both parties understand the contract well, and your release cycles are reasonably fast, you may not have to. If you have long release cycles, or your relationship with the other team is more like the general public (you have no idea how they’re going to use your API), you may choose to program more defensively to provide faster feedback for your clients. I try to use this as the last resort when I’m in this particular situation.

Scenario C: Inside your own team
I rarely find good reasons to program defensively for code that I will ultimately write. It doesn’t really make sense for me to add in additional lines of code where I can easily test all the execution paths of the final code.

Conclusion
This advice is in constant practice. Even when you’re working under Scenario A, it’s only your external APIs that you are probably interested in programming defensively. Delegating to all other private or internal members will often be in the context of Scenario B or Scenario C. Remember the rule that ‘Context is King’, so ensure everyone understands that context before jumping to a particular conclusion about what you should or should not be programming defensively.

« Older posts Newer posts »

© 2024 patkua@work

Theme by Anders NorenUp ↑