Saturday, September 12, 2009

Hello JRack

Today Woody Zuill and myself put some of the finishing touches on a port of Rack to Java, and published it to the world. So let's do a quick show-and-tell to show you why we are so excited.

JRack is an extremely simple yet extremely useful and powerful way to create web apps.

Simple To Create
Let's look at how simple it is to create a "Hello World" web page.
public class HelloWorldRack implements JRack
{
public RackResponse call(Map<String, Object> input)
{
return RackResponseUtils.standardHtml("HelloWorld");
}
}



Wasn't that simple? Just 1 line to pass back the html we want displayed.

Simple To Run

Of course, this has just created it. What if we actually want to have this running on a Web Server, what would we have to do?

We can simply harness it through Jetty (you can also deploy on regular servers like tomcat). Jetty is a simple, standalone Web Servlet container. You can find out more about it here:

We'll add a main method to our HelloWorldRack:
public class HelloWorldRack implements JRack
{
...
public static void main(String[] args)
{
JettyUtils.startJettyRack(80, new HelloWorldRack());
}
}
Now, if we run HelloWorldRack, we will have a fully running rack server serving the HelloWorld page.
Nice and simple. You are up and running with just two lines of code.


Simple To Test & Reuse
But what if you unit test this code? Don't you need a webserver to even run it? NO!
You can call the RackApp directly. Simply pass in what in used by the environment (in this case, nothing). Whats more we've added approval test support so you can simple approve the resulting html.


Put this code into a JUnit test class:
public void testHelloWorld() throws Exception
{
Approvals.approve(new HelloWorldRack().call(null));
}
Okay - simple to write, simple to run, simply to test. There are many sites extolling the virtues of Rack. More and more, the Ruby community is migrating over to it. And now, you can have the same simplicity in the Java world. We'll write more about it later. You can download it here.
I also encourage you to look at the test examples here

Sunday, June 21, 2009

Why Write Unit Tests?

Why Write Unit Tests?

I've made a small video to go with this post






Approval Tests have allowed us to focus on the parts of testing at a much higher level of abstraction. Things that had been too hard to see have become visible. The benefits of Automated Tests are one of the areas that we can now more clearly appreciate. There are 4 main areas programmers can benefit from using Automated Tests (Unit and otherwise)

  1. Specifications
  2. Feedback
  3. Regression
  4. Granularity

Let's talk about each of these.

First, there is Specifications. Everyone knows that good requirements help programmers to produce better software. Well, you can't do better than a running, failing unit test. This gives a clearly defined goal, and provides a executable document of the Users Intent. Focusing on this particular area, has led to the emergence of Behavior Driven Development (BDD).


Fast Feedback. After you know what you want to write, it's very helpful to know that you are fulfilling that requirement. Feedback allows you to know when you have made a mistake, or a wrong turn. Imagine you are driving to Disneyland and you miss your turn. When would you like to know that? Immediately afterwards? 10 minutes later? 6 hours later? Or only when you reach the east coast? Feedback from your test can tell you within seconds if you didn't code what you thought you did. Compare that to sending it to QA and getting a bug report back next week. Focusing on this particular area has led to Test Driven Development (TDD), and is what they are referring to when they speak of bouncing between the red & green bars.

Regression Testing is what most people actually think of when they talk about testing. This is all about maintaining & assuring the quality of the code (by which I mean, is the code working). Focusing on this aspect of testing brings about Acceptance Testing and products like Selenium & Fitness.

And finally Granularity. Imagine the following bug report: "The software has a bug." While this is sufficient to tell you whether or not you have bug free software, it's not at all useful in helping you to remove that bug. (If you've ever done a google code jam, this is exactly what the bug reports look like by the way.) It's nice to have some granularity pointing you to the problem. Something like "if you select user #6 and run a expense report for July 7th, line 15 says $34 when it should say $65". Now that's helpful! Again, TDD is really the area most focused on small granular tests, partly because granularity and feedback are highly related.

Hopefully, you will start to see these 4 features of testing as you continue to unit test. I don't want to place any sort of hierarchy of value on them right now, just point out that they are all valuable. Of course, if you are using approval tests, you'll see how they can aid each and every one of these categories!

Saturday, April 25, 2009

Approving the Weather


How can I test the weather? We have a project where we need to know the weather report. This is possible thru a web API that will give us the weather report. However, we have no control over either the API or the weather. So how do we test this? The traditional approach is to encapsulate all calls to the weather API and then mock it out. The unfortunate part is this doesn't actually test the weather at all. It 'only' allows us to test the entire rest of the app in hopes that the encapsulated piece will work. Let's take a look at a new way to actually test the Weather API using approvals.

Feedback, Regression & Granularity :Unit Tests bring us a variety of things that are important in writing software: Feedback as we are writing the code, to know that we are writing what we wanted; Regression Testing so that once it works, it doesn't get bugs; and Granularity of Feedback, so when something goes wrong, we know exactly where and why it did.


A new approach. Let's look at a new approach that can get us all of these things, quickly and easily.

Feedback: First, we are going to need an encapsulated piece that loads the weather.
This is going to be a small class with two functions.

class WeatherLoader
{
  getQuery();  // returns the query that's used to get the weather
  execute(query); // executes the given query to get the weather
}

Now we're going to write a test that exercises this.

Approvals.approve(new WeatherLoader("San Diego"));

When we run this it will fail as all approvals do the first time and we will see the results of using this API.
Query:
GetCityWeather?city=San%20Diego

Result:
<WeatherReturn>
<Success>true</Success>
<ResponseText>City Found</ResponseText>
<State>CA</State>
<City>San Diego</City>
<Temperature>62</Temperature>
<Unit>Fahrenheit</Unit>
</WeatherReturn>

So now we have the feedback that our API is working.


Regression: Of course now that it's working we need it to work from this point on. Since we have no control over the weather, when we approve this test the only thing that will be approved will be the query, not the result. From now on as long as the current query matches the old query this test will pass. This test will pass without actually running the query against the web service.
This is important so we'll say it again, The test passes if the query hasn't changed.

This means the approval artifact would look like this :
GetCityWeather?city=San%20Diego


Granularity When Things Change: Of course all things change. Let's say in the future someone changes this call so that it returns the temperature in Celsius. Lets take a look at what would happen when you run that test.

Received
Approved
GetCityWeather?city=San%20Diego&unit=Celsius

Result:
<WeatherReturn>
<Success>true</Success>
<ResponseText>City Found</ResponseText>
<State>CA</State>
<City>San Diego</City>
<Temperature>29.4</Temperature>
<Unit>Celsius</Unit>
</WeatherReturn>
GetCityWeather?city=San%20Diego

Result:
<WeatherReturn>
<Success>true</Success>
<ResponseText>City Found</ResponseText>
<State>CA</State>
<City>San Diego</City>
<Temperature>85</Temperature>
<Unit>Fahrenheit</Unit>
</WeatherReturn>


Both queries are re-run: Notice that the temperature of the approved file is 85 not the 62 when it was originally approved. This is because the query is the only thing that is persisted. The result of the query is re-executed at the time of failure giving us the current temperature in the approved section.

Then notice that the query is re-executed for the received side. Through the beauty of diff tools not only do we get the large picture of what's going on but the details and granularity of everything that has now changed.

Testing the Indeterminate : This is a one line test that runs against an external Indeterminate system. It required zero setup and we have zero control over it, yet, in the end we have tests that give us Granularity, Regression and Feedback.

Other possible cases: Databases are another place where being deterministic can be a problem. Of course you can do a large amount of setup to force them to be deterministic. And, of course, this carries a high price in both writing the test and maintaining it. Let us suggest that you just accept that things aren't deterministic and use the above approach.

As a final thought. Agile was created by accepting that requirements change. This approach was created by accepting that not everything we test is deterministic.

Sunday, March 29, 2009

Use Reporter Annotations

Today I implemented a bit of dependency injection for the reporters in the Java version of approvaltests.

The idea is that you might want your own reporters for your project. Things to take advantage of something we didn’t think of. While you could have always done this by calling:


Approvals.approve(ApprovalWriter,
ApprovalNamer,
ApprovalFailureReporter)


I wanted to make it a wee bit easier.

Now you can do it by adding an annotation to any level of the stack calling. For example:


@UseReporter(FailedQuietApprovalReport.class)
public class MyQuietTest extends TestCase
{
public void testSomethingQuiet()
{
}
@UseReporter(FailedDiffTextApprovalReport.class)
public void testWithDiff()
{
}
}

This should make it easier in Java, and will probably appear soon in c#. You can download it now as part of

ApprovalTests Current 0.0.004

Wednesday, January 7, 2009

"Let it BDD" and a mention about approvals

Here's a parody I wrote on testing code that mentions approvals.

Let it BDD