If you live in Detroit, will be visiting Detroit or just want to go to Detroit on November 15th, consider checking out the 1DevDayDetroit conference. It’s a conference for software developers–especially those in the Detroit metro area–that is inexpensive and well worth your time. I’ve been there a couple of times (here’s a review of the 2012 edition) and loved it every time. There’s always a palpable mix of local developers and national names (Bruce Tate and Jennifer Marsman are topping the bill this year).
Most importantly, I learn new things each time I go and meet great people. The software developers of Detroit are proud of themselves and their craft and they show it every time at 1DevDayDetroit. Take a look at the great lineup on the session list. Also, thanks David McKinnon for giving me the chance to speak at the conference this year!
A physician, a civil engineer, and a computer scientist were arguing about what was the oldest profession in the world. The physician remarked, “Well, in the Bible, it says that God created Eve from a rib taken out of Adam. This clearly required surgery, and so I can rightly claim that mine is the oldest profession in the world.” The civil engineer interrupted, and said, “But even earlier in the book of Genesis, it states that God created the order of the heavens and the earth from out of the chaos. This was the first and certainly the most spectacular application of civil engineering. Therefore, fair doctor, you are wrong: mine is the oldest profession in the world.” The computer scientist leaned back in her chair, smiled, and then said confidently, “Ah, but who do you think created the chaos?”
From Object-Oriented Analysis and Design with Applications by Grady Booch. Have a calm weekend while you prepare to wreak more havoc again on Monday.
regex-tester version 0.1 is an open source project that removes the boiler-plate code needed to test regular expressions with JUnit.
Regular expressions often contain business logic that is important to an application yet are rarely put through rigorous, automated testing. That’s unfortunate because it’s generally so easy to test a regular expression (regex, from here on). In many cases, you just want to know that a given string produces a match when the regex is applied to it. That’s easy even without regex-tester but tested so infrequently in real software.
Running the regex against a handful of strings in a JUnit test is superior coverage to none at all. That’s easy to do with regex-tester:
@RunWith(value=RegexTestSuite.class)
@Regex(value="^com.*")
public class BasicRegexTest {
@RegexTestStrings
public static List<RegexTestStringInfo> getTestParameters() {
return Arrays.asList(new RegexTestStringInfo[] {
new RegexTestStringInfo(true, "com"),
new RegexTestStringInfo(true, "com.thewonggei"),
new RegexTestStringInfo(true, "com.thewonggei.regexTester"),
new RegexTestStringInfo(false, ".com.thewonggei"),
new RegexTestStringInfo(false, "www.google.com")
});
}
@Test
public void test() {}
}
Running the test suite above will automatically create and execute a test case for each test string specified. Even that is a lot of typing for when you want better coverage of the regex. If you want to test many more strings, you can put them in a simple properties file like this:
#A couple of out-of-range examples first 1899-12-31=false 2100-01-01=false #These are just the wrong format 05/30/1979=false 05.30.1979=false #... #However many test strings you desire #... #A whole bunch of matching strings 1900-01-01=true 1900-01-02=true 1900-01-03=true 1900-01-04=true
Then the JUnit test gets even simpler:
@RunWith(value=RegexTestSuite.class)
@Regex(value="^(19|20)\\d\\d[- /.](0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])$")
@RegexTestStringsFile(propsFile="src/test/resources/date-format-test-strings-01.properties")
public class DateFormatRegexTest {
@Test
public void test() {}
}
In both cases, the crucial lines of code declare the important pieces under test: the specialized JUnit test suite to use (@RegexTestSuite), the regex to test with (@Regex) and the strings to execute against the regex (either the @RegexTestStrings method or @RegexTestStringsFile). For each string, a boolean value is supplied that indicates whether or not the regex should produce a match.
Therefore, the meaning of
RegexTestStringInfo(true, "com")
is “the given regex will produce a match against the string ‘com'”. If using the properties file
www.google.com=false
means “the given regex will not produce a match against the string ‘www.google.com'”.
These two examples show both the library’s current capabilities and all that is required of you to start automating the testing of your regular expressions. There is much yet to do (e.g. I know you already wondered why you can’t test for the number and content of matches in regular expressions with groups defined) that will come out in later versions. For now, I’m anxious to know about any usage of the library and, especially, what you think needs added or fixed. Feel free to open up an issue on GitHub for any usage problems, bugs or feature requests.
Get the Code
The code is under the MIT License and is hosted on GitHub here. See the project’s README file for more details.
Get the JARs
Maven coordinates:
<dependency> <groupId>com.thewonggei</groupId> <artifactId>regex-tester</artifactId> <version>0.1</version> </dependency>
Or, download the JARs directly from Maven Central here.
Learning jQuery: A Hands-on Guide to Building Rich Interactive Web Front Ends is written by Ralph Steyer, and published by Addison Wesley, © 2013, (paperback), 978-0-321-81526-2, 495 pp., $39.99 US.
Learning jQuery is a single entry in the pantheon of jQuery books that introduce web programmers to this JavaScript framework (Amazon has over 20 such books available as of November, 2013). Steyer’s book follows the same pattern as similar books I’ve read and explains the core functionality provided by jQuery: the jQuery object, selectors and filters, how to manipulate the DOM (Document Object Model), AJAX (Asynchronous JavaScript and XML) and animation. These and other topics are explained with clear, knowledgeable descriptions and ample code samples and tutorials that include both HTML and manipulation of it through JavaScript and jQuery. Steyer includes an adequate number of illustrative screenshots too.
The book begins with jQuery fundamentals and progresses to advanced topics at the end. Advanced topics include jQuery UI, plug-ins and jQuery Mobile. The fundamentals are the focus, however, taking up about ¾ of the book. Despite the tilt towards developers new to jQuery, this book assumes that the reader is already competent with JavaScript and HTML programming. In fact, tackling the plug-ins chapter will be the most difficult for a reader who lacks adequate experience programming in JavaScript.
Even though this book follows the same formula as many other books about jQuery, it is a worthwhile read. Steyer obviously understands jQuery thoroughly as he frequently describes common pitfalls in using the framework and how to avoid them. The information in the book is far more illustrative than the reference information available on www.jquery.com and far more instructive than the thousands of sketchy examples and explanations you dig up with a web search.
Despite having experience with jQuery in production web apps, I learned quite a few new tricks from this book. I also found that I gained a deeper understanding of how jQuery works, which I believe will make my future jQuery development more precise and effective.
Assumptions + important edge cases. That’s it. That’s all I consider when I’m testing my code. But knowing that you aren’t satisfied with such a trivial explanation, I’ll explain.
Since I discovered Test Driven Development (TDD) a handful of years ago I have followed it, practiced it, tried to adhere to its loose tenants. There’s no question that, in order for me to produce code that I am comfortable releasing to QA and users, TDD is the sanest path we software developers now have to follow. The outcome of my embrace of and consequent struggles with TDD is that I have developed an intuition about testing my code that only now can I explain simply as assumptions + important edge cases. That formula is how I determine when I’ve done enough testing.
Assumptions
Assumptions are the things you take to be true about how your code should work. That covers both the input and output side of the code you are currently working on. Since TDD drives your work down to the individual functions, your assumptions are the current list of facts you possess about what might be input to your function and what output each of those inputs should produce.
You cannot and should not test functionality that you do not need—that is, perhaps, the most fundamental tenant of TDD. The popular aphorism You’re Not Gonna Need It (YAGNI) applies here. So once you are satisfied that you have collected all the facts about some code you need to write, you only need to work on it until all of your assumptions are codified in that function. Then stop. If you don’t, you’ll start testing for functionality that only you dreamed up for the code to do. The assumptions you have gathered, most likely through Agile processes that are well documented elsewhere, represent the entire universe of outcomes that anyone, at a static point in time, care about the code producing. If the code does something outside of that tenuous list, so what? The assumption is that no one will ever ask the code to do that extra work anyway. You and the people who helped formulate the assumptions have a contract that only those assumptions matter.
Important Edge Cases
Of course there are always those conditions that your business sponsors and business analysts and such never think of—stuff that only programmers care about. Those are the edge cases. You have been trained to worry about the extremes in the input range when writing a function.
I worry about that too when I’m writing unit tests. Even when the edge cases aren’t covered by the list of assumptions. For every function there are those input values that just wreak havoc when not handled properly. Maybe, for instance, you know that letting a null value be operated on by your function is an edge case that you absolutely must deal with. Even though handling null values in some graceful manner might not be in your list of assumed behaviors for your code, you know that you have to write code to handle that situation in a reasonable manner (Want to make the world a better place? Give it fewer NullPointerExceptions to deal with).
The null value example is an instance of an important edge case. It’s just one of a small list of edge cases that you should care about though. If you wrote the code to match your assumptions already, then all you have left to do is handle the few very important edge cases that keep your code from behaving particularly badly. And that is a good place to stop testing and writing code.
There’s Always a Caveat (or two)
The caveat that has probably already caused you to think about some scathing comment to add to this post is that whole assumption part. It’s just so damn open-ended. That’s true but I’m ok with that (you don’t have to be though). The idea is that I can grasp the idea of figuring out what I, at any given time, assume about the operation of a piece of code. I can write out that finite list of assumptions and test each one. When I add the edge cases on the end of that list, I can test the code for everything I know about it’s expected functionality. In other words, assumptions + important edge cases is merely my heuristic for figuring out when I’m done writing and unit testing a piece of code.
The other thing about assumptions is that they can include more than just business-driven functionality. Depending on your development team, they could include standard programming practices around security, code monitoring, performance, etc…. You might have a common list of assumptions that applies to every function you write just to satisfy the software development standards in your organization.
Finally
You will get some overlap between the two concepts of assumptions and important edge cases. The Venn diagram (I love these things) below shows that some of your assumptions will cover some important edge cases. It’s up to you to sort all of that out and just pick the edge cases that are still important to consider but didn’t happen to be on the list of assumptions.
That’s the attitude I take towards writing code and unit testing it, for better or worse. It’s an internalization of a lot of ideas that I have both learned about from others and worked out for myself. It’s not a prescription for the right way to write code nor is it an admonishment of how anyone else does their work.
