I act in cycles. I am inspired by and I work on something new, and that inspiration shifts around. I also have a tendancy to drop and pick up projects in cycles. In the past I've dropped ideas for long enough for all effort to become meaningless given enough time, but these days I've been careful to cycle back to earlier ideas to scrape off the cruft and re-launch the project.
When I re-launch a project, I often find myself bringing significant maturity to the table. I have new ideas and techniques, and I take a kind of sick pleasure in redoing much of my previous work. This isn't just a programming thing.. it applies to anything that I've been working on.
So when I cycled back to programming as an overall topic, I decided to research languages and techniques again. I went through a bunch of languages, most of which I still have a significant amount of notes from previous reviews. I even sat down and started hacking around with Python. But the more time I spent on any of that, the more I missed Ruby.
Ruby is crap. It's garbage collection, threads and IO suck. It's slow. It has no compiling. It's crappy in so many ways, but because of it's syntax and community.. it's fun.
I can't help it. I look through language after language after language.. and I miss Ruby. So I decided to look into Ruby some more. I only have basic competence with it, but I've still managed to create some impressive (to me) stuff with it.
While I know that the syntax and community are extraordinarily important for a programming language, I also know that underlying philosophies, techniques and tools are also important. So - as I have many times in the past - I went looking into all of the foundation of what I know and how I work.
I had done some playing with unit testing in the past, but somehow I ended up straying from that previous knowledge and work by creating my own automated testing systems. I don't really know how or why I forgot my earlier knowledge either. So when I realized this recently, I found some old code and stripped it to create a simple example for myself. I'll make another blog post with that, to help out the other Ruby Test::Unit newbies. Actually, automatically linking my notes system with blog posting is something I'll eventually get into.. when I build my monstrosity of a CMS/Wiki/Weblog. One day.
At any rate, when researching better testing and testing tools, I came across Behaviour-Driven Development (BDD). I've had exposure to Extreme Programming, Agile Development and similar stuff, so BDD isn't particularly new to me. But I did bump into Cucumber, and I started to get more interested.
I figure that if I step way back, and get into some sort of spec-writing like that, then I can help direct my attention and improve my programming. This kind of work is also programming language-agnostic, so I could much more easily switch to whatever language I was goofing around with at the time.
But one of the biggest reasons for my wanting to get into it is to help with the loomingly-large "compiled website" project which I was working on. I say "was" because I managed to drop that project and skip a number of opportunities to get back into it. Nice specifications would break the project down, making the whole thing much less intimidating to set down and later pick back up.
Specs could also help me better understand my priorities .. to distinguish "must have" features from mere wishes.
At any rate, I'll do some more research on all of this, and hopefully I'll remember to blog something about them.
I'll end this with my quick interpretation of What are the Qualities of a Good Test?
Isolated from the outside world
- Doesn't talk to the database, communicate across the network, touch the file system or do special things to the environment
- It is written gray-box, i.e. it reads as if it were black-box, but sometimes takes advantage of white-box knowledge. (Typically a critical factor in avoiding combinatoric issues.)
Isolated from other tests
- Unaffected by the presence, absence, or results of other tests. No dependencies. Self-contained.
- Order Independent and Isolated - it should be possible to run the tests in whatever order the test runner chooses.
- be able to run at the same time as other unit tests, even concurrently with itself
- It does not test the object inside the running app, but instead in a purpose-built testing application.
Repeatable in every aspect
- Test your boundary conditions and Always keep your tests passing at 100%.
- Social / Convenience:
- Simplifies design and improves productivity. Improvements in code quality and a reduction in bugs are an important side effect.
- Intention-revealing - the best unit tests make it clear to the reader how an objects API is intended to be used.
Easy to Setup
- Short, typically under a dozen lines of code.
- It invokes only a tiny portion of the code, most usually a single branch of a single function.
- Quick and easy to write
- It is generally written before the code-change it is meant to test.
Fast to run/complete
- As above, it's short and invokes a tiny portion of the code.
- Unique (providing confidence not provided by other tests/uncorrelated with other tests)
- Coded to the same standard as shipping code, i.e. the team's best current understanding of coding excellence.
Promotes code commits: In combination with all other microtests of an app, it serves as a 'gateway-to-commit'. That is, a developer is encouraged to commit anytime all microtests run green, and discouraged (strongly, even nastily) to commit otherwise.
I have no idea what that means:
It avoids most or all usage of 'awkward' collaborators via a variety of slip-and-fake techniques.