Today I had a conversation with @WoodZuill where an extremely rare situation came up. A situation where you can practice TDD with Completeness of the possible user scenarios.
I vBlogged my thoughts here:
I'm using the LegacyApprovals package (which I wrote, download below) here to test every combination of the possible parameters.
It's worth pointing out this is NOT what the LegacyApprovals package was actually made for. It's design to test a lot of cases, but not every case.
Links:
Approval Tests (Legacy Approvals is part of the standard ApprovalTest download.)
Note: In C# it's ApprovalTests.Combinations.Approvals.ApproveAllCombinations()
In Java it's org.approvaltests.legacycode.LegacyApprovals.approve()
More Agile Thoughts
Tuesday, December 7, 2010
Tuesday, October 12, 2010
Java Lambdas
I mentioned last month that I had found a way to add lambdas to the current JDK. I struck me that perhaps many of you had not used lambdas before, so I thought I make a small tutorial on one of the common uses:
Sql like queries against your objects.
Let’s start with the most basic,
Order By
If you where writing an Sql query to get people based on age, you would say
Select *
From People
Order By age
or perhaps
Select *
From People a
Order By a.age
If you had an array of people in C# this would be written as
Linq.OrderBy(people, a => a.GetAge())
Now, to do this in Java is a little more compacted, but very similar
Query.orderBy(people, new S1<Person>(a){{ret(a.getAge());}});
let’s look at some more examples.
Select
Let’s say you wanted to get the names of all the people, you’d write a query like
Select a.names
From people a
Now, let’s translate that into java
Query.select(people, new F1<Person, String>(a){{ret(a.getName());}});
of course, with java you can select more than just primitives, if you wanted the Addresses, you could do that too.
Query.select(people, new F1<Person, Address>(a){{ret(a.getAddress());}});
you can even do transforms
List<Student> students =
Query.select(people,
new F1<Person, Student>(a){{ret(new Student(a.getName(), a.getAge()));}});
let’s move on.
Where
Of course most queries wouldn’t be much use without the where statement. Let’s find the all the A+ students
Query.where(people, new F1<Person,Boolean>(a){{ret(a.getGradePoint() > 3.8);}})
btw: it might be worth expanding the code a bit. I’m cheating a little to help with understanding. For lambdas to work in Java, there is an unfortunate side effect. It gets call 1 extra time. Specifically the first time. So you need to pass in an object. That object in the examples above is pre-defined as ‘a’ but in practice I usually use a blank object
new F1<Person,Boolean>(new Person()){{ret(a.getGradePoint() > 3.8);}}
unfortunately, you can’t use ‘null’ because it would throw a null pointer exception. I also use the null object pattern a lot: Person.Null
of course there are some standard calculations that are nice too.
Average
what if you wanted the average length of a person’s name?
simple
Query.average(people, new F1<Person, Number>(a){{ret(a.getName().length());}});
of course for averages, the return type F1<InputType, ReturnType> must be a Number.
Min
by now you probably see how easy it would be to get a minimum or maximum, so let’s up the lambda by using a local variable. Let’s find the person who got the closest grade to sally.
final Person sally = getPersonByName("Sally");
Query.min(people, new S1<Person>(a, sally){{
ret(Math.abs(a.getGradePoint() - sally.getGradePoint()));}});
You might have noticed the final key word, java requires that for locals to be used inside of the lambdas. you might also have noticed that this time I had to pass in 2 parameters (a, sally) to use sally inside you need to add her as a parameter as well. Lastly, I used the class S1<Input> here instead of F1<Input, ReturnType> this is just a convenience class which extends F1, since any where, min, or max queries require the output to be a Comparable object, like Integer, String etc.
Well, I hope this got you interested in what lambdas can do for you. Check out the video and download the jar to get started at http://bit.ly/lambdas
Happy coding,
Llewellyn Falco
Monday, August 30, 2010
AssertNotNull
Recently had some issues with the method assertNotNull(object) because it doesn't help with any of the 4 principles of TDD.
I vloged my thoughts here.
Finally, here's the code I was looking at.
and check out www.approvaltests.com
- Specifications
- Feedback
- Regression
- Granularity
I vloged my thoughts here.
Finally, here's the code I was looking at.
and check out www.approvaltests.com
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.
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:
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:
I also encourage you to look at the test examples here
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 JRackWasn't that simple? Just 1 line to pass back the html we want displayed.
{
public RackResponse call(Map<String, Object> input)
{
return RackResponseUtils.standardHtml("HelloWorld");
}
}
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 JRackNow, if we run HelloWorldRack, we will have a fully running rack server serving the HelloWorld page.
{
...
public static void main(String[] args)
{
JettyUtils.startJettyRack(80, new HelloWorldRack());
}
}
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 ExceptionOkay - 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.
{
Approvals.approve(new HelloWorldRack().call(null));
}
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)
Let's talk about each of these.
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!
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)
- Specifications
- Feedback
- Regression
- 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.
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.
When we run this it will fail as all approvals do the first time and we will see the results of using this API.
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.
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.{
getQuery(); // returns the query that's used to get the weather
execute(query); // executes the given query to get the weather
}
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>
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><ResponseText>City Found</ResponseText>
<State>CA</State>
<City>San Diego</City>
<Temperature>62</Temperature>
<Unit>Fahrenheit</Unit>
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:
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:
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
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
Subscribe to:
Posts (Atom)
