Using JUnit 4 Theories to Test Contracts 5

Posted by Berin Loritsch Tue, 09 Feb 2010 16:08:00 GMT

I’ve been putting off upgrading to JUnit 4 for a while. After all, just how much does it really buy you? It turns out, that JUnit 4 grants you a number of advantages that weren’t available with JUnit 3. One of those features is currently in an experimental phase: Theories. Theories let you specify a bunch of data points, which can be applied to each theory in your class. For example, if you want to test some boundary conditions on a class, you can make it happen easily like this:

@RunWith(Theories.class)
public class VerifyMyAlgorithm {
    @DataPoint
    public static int limLow = 0;

    @DataPoint
    public static int limHigh = 3000;

    @DataPoint
    public static int belowLimLow = limLow - limHigh;

    @DataPoint
    public static int aboveLimHigh = limHigh * 2;

    @DataPoint
    public static int median = (limHigh - limLow) / 2;

    @Theory
    public void verifyTwoParameters(int first, int second) {
        assertInRange(limLow, limHigh, MyMath.algorithm(first,second));
    }
}

I wanted to keep it simple just so you can get the basic idea. Essentially the @RunWith() annotation changes the basic behavior of your test class. It enables the use of the following annotations: @DataPoint, @DataPoints (more on this later), and @Theory. The data points you specified are collected and matched together so that each unique combination of datapoints is applied to the parameters in your theory. If your theory takes one parameter, the theory is run once per data point. If your theory takes two parameters it is run with every unique combination (15 times in this case).

But wait, there’s more! Sometimes we want to generate our datapoints with code. For example, we may need prime numbers up two four significant digits, or we need to initialize our data. In order to do that, we can use the @DataPoints annotation. The return type of your static method will be an array of datapoints. I could rewrite the example above like this:

@RunWith(Theories.class)
public class VerifyMyAlgorithm {
    @DataPoints
    public static int[] attemptLimits() {
        return new int[] {0,3000,-3000,6000,1500};
    }

    @Theory
    public void verifyTwoParameters(int first, int second) {
        assertInRange(limLow, limHigh, MyMath.algorithm(first,second));
    }
}

The DataPoints functionality in concert with the theories are the foundation of what is ncessary to automatically test the contracts of each service. I’ve thrown together a rudimentary class scanner that uses reflection to iterate over classes in the classpath to determine if they match the criteria for the service. For example, you can look at all classes that implement an interface, or extend a base class, or even have an annotation. From that list of classes we can test the contracts of the implementations. The nice aspect of this approach over creating a base test class and extend it manually for each implementation we write, is that it automatically finds the new implementations for you. I have yet to release the class scanner, but you can implement your own pretty well. Here is an example of how it would be used:

@RunWith(Theories.class)
public class EnforceLocakableContracts {
    @DataPoints
    public static Lockable[] collectImplementations() {
        Collection<Class<?>> klasses = new ClassCollector()
                .assignableTo(Locakable.class).recurse().collect();

       Lockable[] implementations = new Lockable[klasses.size()];
       int i = 0;
       for (Class<?> klass : klasses) {
           implementation[i] = klass.newInstance();
           i++;
       }

       return implementations;
    }

    @Theory
    public void lockShouldApplyToOneUser(Lockable lockable) {
        User one = TestSupport.createUser("one");
        User two = TestSupport.createUser("two");

        assertFalse(lockable.isLocked());

        lockable.acquireLock(one);

        assertFalse(lockable.canAccess(two);
    }

    @Theory
    public void lockableShouldBeAccessibleByLocker(Lockable lockable) {
        User one = TestSupport.createUser("test");

        assertFalse(lockable.isLocked());

        lockable.acquerLock(one);

        assertTrue(lockable.canAccess(one);
    }
}

So on and so forth. Just add a new Theory for each aspect of the implementing class you want to test. With this approach, testing implmentations of an interface is essentially future proofinf yourself against lazy programmers. There are some aspects that this approach doesn’t handle well just yet, such as complex setup for each object.

As of JUnit 4.7 there are a few problems, but they are not insurmountable:

  • You see one pass/fail per theory (not per implementation)
  • Failures do not show what implementation failed (fixed in the stacktraces provided by JUnit 4.8.1)
  • The first failure kills future tests. You may have errors in multiple implementations but you won’t be able to find it until you fix the first implementation.

The best thing is to upgrade to JUnit 4.8.1, which is still not available in Maven repositories yet. However, you can also use the System.out approach to find out what the last thing tested was.

There is a feature request for theories to allow you to run them discretely. I.e. each run gets displayed in your test report individually with the parameters supplied in the test name. That will address the shortcomings and make this an even better approach.

Wicket, Spring, Hibernate, Testing... Yeah, it's like that.

Posted by Berin Loritsch Tue, 10 Nov 2009 16:57:00 GMT

I have to deliver a product using Java. Due to politics and approved baselines, etc. I can’t use Ruby on Rails. I’ve done my own thing in the past, and so this time I decided to save some time by incorporating Wicket, Spring, and Hibernate using auto-wiring, and attributes. Getting it all to talk together took the better part of a day, but I got it to deploy and run fine.

I’m an avid unit tester, so I was happy to learn that Wicket has some facilities to make testing a bit easier without forcing you to create your own Servlet mock objects. I was really happy about that, so I decided to try it out. Unfortunately, I ran into a rather nasty roadblock. Spring was complaining about the ContextLoaderListener not being loaded. It’s defined in my web.xml file, but the autowiring requires the spring context to be loaded in a particular location. Finding out how to fix that problem took me the better part of half a day.

The problem is that Wicket hides the ServletContext object away from you. You can’t add values, because the test framework is initialized on construction. Attempts to obtain the ServletContext and manually add the Spring web application context just weren’t working. That’s because it is configured when the WicketTester object is constructed and ignored after that. I needed to be able to inject my own ServletContext with the Spring integration taken care of. I finally figured it out here, because the forums just working for me. Please note that this is tested with Wicket 1.4 and not any of the earlier versions:

final MyApplication app = new MyApplication();
tester = new WicketTester(app) {
    @Override
    public ServletContext newServletContext(final String path) {
        // web context
        ServletContext context = new MockServletContext(app, path);
        // spring context
        XmlWebApplicationContext spring = new XmlWebApplicationContext();
        spring.setServletContext(context);
        // configure spring
        spring.setConfigLocation("classpath:application.xml");

        // put spring where Wicket can find it
        context.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, spring);
        return context;
    }
};

So let me explain what I did. I created an anonymous subclass of the WicketTester class so I could override the factory method “newServletContext” to inject my spring configuration. Done properly, the wicket folks can take care of this by providing a SpringWicketTester subclass in their spring integration library. The SpringWicketTester would let you specify the location of your Spring configuration file, and it would override that method for you. For now, you’ll have to do it yourself.

I put the WicketTester initialization in a subclass of the JUnit TestCase class. That way I don’t have to repeat myself for setting up my Wicket testing.

Diving in to film testing and random thoughts

Posted by Berin Loritsch Fri, 12 Jun 2009 14:15:00 GMT

I’ve been having mixed results in the darkroom, so I’m in the process of at the very least improving my consistency. Part of that process is testing my film and development time for what I like to shoot. It hurts my feelings to use so much precious 4×5 film for the purpose—but I’ve learned something. My instinct to shoot at half the rated speed for my Fomapan (or Arista.EDU Ultra) film was right on the money. However, the development times for Rodinal 1:50 I got from Digital Truth are way off for the way I develop. When I tray develop, I am constantly agitating and rocking the tray back and forth. As a result I have way more contrast than I should.

How do I know? Well I do have an X-Rite 810 densitometer, which can be found on ebay for a reasonable cost. I don’t plan to use it all the time, but while I’m getting my process under control it’s becoming quite valuable. I found the minimum exposure that produced .1 over film base+fog which gave me an ISO of 100 for the Fomapan Creative 200 film. Great. Now, I’m trying to find the right development time that will give me a density between 1.25 and 1.35 for a Zone VIII exposure. The first time through I accidentally made the exposure for Zone VII (that’s zone 7 instead of 8), and used the recommended 10 minute development time for Rodinal 1:50. The density I got was about 1.89. So let me spell it out, it was way above what it should be. My second attempt corrected the exposure (more exposure, more density) and halved the development time to 5 minutes. The density I got was now 1.69, which is still way above what it needs to be. My two choices now are to dilute the developer or to use less agitation. If the time is below 5 minutes you will see some variance in results depending on how quickly you stop the development. I’m not done testing yet, and it is going much slower than I’d like because I don’t have but a few minutes at night to do one test.

Film testing is not a fun task, but in many ways I think it is necessary to go through to at least establish your custom times. The goal is to spend less time fighting my negatives to get a reasonable print and more time enjoying the results. The problem I’m running into is a lack of consistency in my negatives or detail in the highlight areas. The negative has it, but the paper can’t reproduce it readily. My scans came out nice but the prints were a lot more contrasty. Now I know why.

After I get my Arista/Foma film development process down, I may experiment with using my Silvergrain Tektol to develop the film. I’ll start with paper strength at 5 minutes and adjust density by adjusting my dilution or time.

Untangling a Mess

Posted by Berin Loritsch Fri, 19 Dec 2008 15:22:00 GMT

Sometimes through the best of intentions meeting customer expectations and getting critical functionality out the door we create a mess. So what happens if the organic growth of complexity reaches the critical point? Obviously it needs to be addressed, or this rats nest of code will seriously impact your ability to maintain the project, or worse it impacts the performance and stability of the application.

There are all kinds of rats nests that lay in wait ready to spring into your code. These range from overly complex processing to a series of database updates and reads that should be packaged up and optimized better. These complexities come from performance optimizations, or from needing to track more meta-information about your project’s data. Sometimes the complexities start out with a poor design, but most of the time it comes from a decent design that just wasn’t cleaned up.

Analyzing the Problem

The first thing to do when analyzing a rats nest is to document the before and after states. What should the data look like when we perform a certain action? Any solution that breaks this contract will cause problems in the application. Once we’ve documented what the application is doing that we don’t expect, the first thing to do is to remove those changes from the system. Since we are in the documentation and analysis stage, we just have to cross out those actions from the change package.

The next step is to figure out how to cleanly make sure that the end result is what we expect, and that there is no surprising work done. Having simple, well defined packages of change will help you get a grasp of the next steps you need to take. It can also provide its own performance improvements as the system isn’t doing unnecessary work.

The final step of analysis is to figure out how drastic the change has to be. Can you get away with a few key changes? Do you really need to rewrite whole sections of code? In essence, this is where we assess the risk of the proposed changes. Are there ways to mitigate the risks and go through with it? Can we perform the work in stages spread out over more than one release? How long is it going to be to make these changes? How long would it be to pursue an alternate solution.

Solving the Problem

There are several ways to manage the structure of your code, and one size definitely does not fit all. Something like Design Patterns by the Gang of Four (Gamma, Helm, Johnson, Vlissedes) provides several common object oriented solutions to common programming problems. Too many people have used this book as the “Holy Grail” of software development and have erected crystal cathedrals of software in homage to the GoF. It’s a tool to help you think of ways of solving a particular problem. It’s not the doctrine of all software design. Nevertheless, don’t throw the baby out with the bath water. You can use solutions listed in that book, but implement them differently than they suggest.

Perhaps the right solution would be the “command pattern” which lets you create an object that performs the logic for a change. Assuming you have a fairly low number of changes, this can be a very clever solution. However, if the number of command objects suddenly explode into a large number it can introduce its own management problems. The command pattern can be an attractive way to deal with a finite set of complicated database transactions.

Maybe you need a proper Finite State Machine (FSM) and use the “state pattern”. With each state having a well defined start and end point, you can easily test the individual states in isolation. Just don’t forget to test the whole set together.

No matter how you choose to solve the problem, you have to ensure that you have adequate testing. Make sure that the changes you need are the only things being changed. For example, nothing else should be touched with your information than what you intended to touch. Being creative, you can build a testing infrastructure that extends your unit test tool to check whether values have changed that should remain the same. In fact unit testing is even more critical with cleanup work.

Testing and Choosing Your Process

Posted by Berin Loritsch Wed, 09 Jul 2008 13:27:00 GMT

Process . It’s a dirty word around some people. All it means is “A way to get something done”. Nothing more, nothing less. However, we have things like ISO 9001, CMM, Agile, XP, Scrum, etc. all presenting themselves as the way to develop your software. Each has it’s benefits and drawbacks, and each works with a different kind of client. If you hear anyone tell you that their way to do things is better, faster, etc. than anything you are doing without knowing the details you know you are dealing with a snake oil salesman. Even within the CMMI framework, there is a lot of leeway for you to have a heavy process or an agile process.

Process is just a way to get things done.

I’ve heard of some people’s software development process as “write a little code, run the app, write a little more code, and so on.” That process can work for some people, but not for everyone. I happen to be a fan of Test Driven Development coupled with Continuous Design—a fairly Agile approach. However, I know there is a lot of people who just can’t think that way. That’s fine. We just probably won’t be working on the same projects.

Manual Process vs. Automatic Process

The first aspect software development process is finding the right ratio between things you do manually, and things that the process takes care of for you—as a side effect of just doing the process. We programmers tend to be lazy, and so we do whatever is easiest on us to do. Ironically, we are so lazy sometimes that we would rather not invest the time to set up the automation unless there is a clear reason to do so. It’s actually a good thing because too much automation can get in the way of making changes. Of course, manual processes don’t scale very well.

Anything you do manually is something you have to remember to tell someone else.

When you are working alone, you can take all the shortcuts you want because you don’t have to explain what you are doing. The problem comes when you add someone to your team. All the things you were doing you now have to explain to someone else and get them to do it, or it won’t get done. Heck, sometimes you either forget a step even when you are by yourself. It’s for that reason we even consider automation. The places where we have the best benefit from using tools include configuration management and creating builds. I wouldn’t imagine writing any code without version control software of some sort even if I’m by myself. The versions are managed for you so you can go back to a release to reproduce issues if necessary, the code is backed up and managed beyond your own hard drive, and as soon as you add more people to the team it takes care of merging code for you. The benefits far out way the alternative. Creating a build might be easy or difficult depending on the structure of the complete application. The build process will create your distributables and run any tests you have included. Tools include Make, Rake, Ant, etc. and have differing levels of complexity. The important aspect is that you aren’t relying on someone else to make the identical configuration settings in their IDE to get the same executable. It’s all taken care of by the script.

There are other areas where automation can help out, but it does depend on the culture of the project, and the ease of setting it up. How do you manage your issues? Many tools generate reports just from how you use the application. You can show how quickly you are burning through your issues, and you can see how fast or slowly you are approaching the completed release. Sometimes you can add flags to the tool to notify you if values are outside a certain set of parameters.

Testing your Process

In any software project there are number of things you have to keep track of, and your process (the way you get things done) should make it easier. So how do we know if the process is working or not? You have to start by identifying what’s important. What is going to make your client lose confidence in you? If you do nothing else, what has to be done? Do you really need full Earned Value Management (EVM) or can you get away with proving you are making progress at the expected rate. Many times it’s enough just to know when you are falling behind. One thing that’s a must in any process is that you produce the deliverables.

If anything important slips through the cracks, your process is broken. Period.

Once you’ve identified the important stuff you can’t miss, you keep track of it. So how do I do that? You do your process. If you can ensure your process takes care of tracking the important stuff, all the better. Keeping track of something is what some people call “metrics”. The thing I don’t like about metrics is that you are essentially associating numbers to parts of your process. Numbers people like metrics because they have something to play with. The reality is that numbers don’t mean anything at all. Trends mean something, numbers don’t. If you are getting better, (i.e. your trend is going in the right direction), your process is working. If you are getting worse, (i.e. your trend is going in the wrong direction), your process needs to be fixed.

So how do you track trends, and when does a change really mean something? The tricky thing with trends is that if your sample is too short you might make adjustments that are not warranted, but if the sample is too long you won’t make the adjustments soon enough. There is no substitute for your gut feeling when you are just starting to track how well your process is working. The trick is not to be alarmist, and make proper adjustments. It’s like learning to drive for the first time. You either make too many large adjustments and overcompensate, or you make too many small adjustments. The good news is that you aren’t going to crash right away.

There are a few things that process people (you know those heavy process proponents) forget to look at while they are testing their process. How much work is needed to feed the process, vs. how much work is spent actually making deliverables? If you need 5 people that do nothing other than track the project’s progress when you have a team of 3 developers generating code, there’s something a little off-kilter. Notice that I said the 5 people are feeding process. Ensuring you have a quality product is work that’s done for the deliverable. Ensuring you have a quality code review is not. That’s “meta-work” or work about work. It’s stuff done to feed the process. Even the discipline of code reviews can be brought into question as long as you gain the benefit another way. A cod review is merely a means to both mentor other team members and ensure the quality of the product. If your process handles those benefits in other ways, then you don’t need a formal code review.

Fixing a Broken Process

If you are like me, then you really won’t wonder about how you fix a broken process. You just make the changes on the fly and the process evolves. You know these “lessons learned” meetings people have that dig up all the surprises and issues that we ran across? Are you merely identifying lessons, or are you actually learning them? If a technology choice turned out to be bad, how can you make sure the same mistake doesn’t happen again?

You haven’t really learned a lesson unless you did something to correct it.

If you discover that something you are doing isn’t lending any value whatsoever, you shouldn’t be doing it anymore. The advantage of changing your ways vs. logging issues is that it actually fixes the cause of your heartburn. Logging an issue helps you vent and get things off your chest, but it’s still there until you actually do something about it. That’s why the traditional “lessons learned” meetings don’t work. They let people vent, and then nothing is done about it. “Yeah, that was painful. We’ll be doing it again next go ‘round too.”

One of the biggest lessons I learned is that waiting until a “lessons learned” meeting to vent is waiting too long. If you are experiencing some heartburn now, fix it now. If you spend more time collecting numbers than you do using those numbers (um, I mean metrics), then just how important are those numbers in the grand scheme of things? Can you find another way of collecting those numbers? It’s funny, but machines are much better about collecting them than people are. There’s probably some way of making those numbers be a byproduct of how you do things. In essence, if you can make your tools support the way you do things and keep track of the numbers for you, you are better off. As long as you are depending on people to actually log numbers, you are depending on the most unreliable source for numbers. It’s a pain to keep track of them. Even if we only need to type them into a form, it’s still a pain to keep track of them. Let your tools do all the data entry for you.

The bottom line is prove you need to change, change, and then prove your change is helping. It’s TDD for process. If you see a bad trend and the process is the culprit, prove it. Then decide what a good trend would be. Make your changes and see how close you are to the results you want. You may find that your change is “close enough”, in which case you are done. Of course, you may find that you just introduced more work than the way of doing things, in which case you revert back or make a different change.

Safari Take 2: Call for Testing 1

Posted by Berin Loritsch Thu, 10 Jan 2008 12:59:00 GMT

Now that I have a Mac at home, I’ve had a little more experience with Safari. For the short review: I had to install Firefox as well. Safari is a nice little browser, and for most things it looks and works quite well. However, it does have a major failing if it wants to unseat Internet Explorer and Firefox from browser dominance as Jobs desired. Not all sites work with it—even those that work in both IE and Mozilla based browsers. That’s not good, because sites that don’t work include the likes of WikiSpaces and Aurora Loans. WikiSpaces gives you reduced functionality, so you have to know Wiki markup to format the page properly. However, Aurora Loans won’t even let you log in. How can you pay a bill if you can’t get in the site?

What’s the cause of this failure? More than likely it has to do with some JavaScript that isn’t supported in Safari but it is in the other two browsers. Perhaps the DOM is not consistent? I can’t be sure. However, I’m almost positive (without popping up ‘View Source’) that Aurora Loans has some JavaScript evoked on form submission. If Safari is going to unseat its competitors, it should at least match what Firefox’s JavaScript support can do.

I’ve installed Firefox so that I can use some of the sites that I can’t use properly on a Mac. But here’s the rub, some sites apparently don’t work properly with Firefox. I’d have to look more into it, but my wife was more than frustrated at the time so it just wasn’t going to happen. The site she was using sends e-invites for jewelry parties and such. For now we have to use two browsers, but that’s just not fair to us the users.

First, Apple needs to test and fix its browser against sites that work in Mozilla and IE—giving more attention to sites that seriously impact users such as banking and loan sites. Perhaps the ECMA Script and DOM standards should have test suites to validate the JavaScript implementations on all browsers. I don’t know, but something needs to be done.

Second, for the rest of us designing sites to be consumed on the internet, we need to include Safari in our cross browser testing. It’s easier to build upon cross-platform support from the ground up, rather than retrofitting libraries to include browser support. However, by the same token, there needs to be a way to test the libraries in the browsers. That way you can know what does and doesn’t work simply by running those tests. Simple things like Prototype work without any problems, so I’m unsure what Aurora Loans is doing that doesn’t work.

I don’t have anything against Safari, and it’s a decent browser. I personally still prefer Firefox, but then again I am used to it. When I put my user hat on, I just want things to work. I’m pretty confident in Firefox’s ability to work, but not so much for Safari. Let’s just make things work—whatever it takes.