Friday, 1 March 2013
Web project specifications
Monday, 24 December 2012
Testing Existing Code
When beginning Unit testing it is hard to find a good starting point. There is a lot of theory and some very good points of reference, but what about actually getting down and dirty & hands-on as so many of us love to do? Here are some ideas, and pointers on how you might want to think about starting out with TDD and Unit testing an existing system. I am a PHP Developer, primarily working on an Apache/MySQL stack, however I would assume these principles can be carried over to any project, if you substitute the appropriate technologies.
I will assume you have been through a few basic tutorials on PHPUnit or are at least familiar with a Unit Testing framework of some description. I won't go into a step by step set up but might assume you know a few things.
1. I would begin by getting a local development copy set up and running. Using XAMPP and MySQL, get a copy of your web application running off of localhost, even if it only has a few test rows of data and is stripped down, or whether it has lots of data isn't really relevant, the fact is I prefer to have a local copy for working on.
2. Obtain your PHP testing framework and get this set up into a directory above your public htdocs folder. I would begin by placing my phpunit files in a folder somewhere not publically accessible, be this above your web directory (htdocs/public_html/www), or within a secure members area or administration area. Not that I intend to have anything secretive in there, just that I feel it's good practice. I would simply create a test class that does the very basic - doesn't even interact with my application and simply asserts that true is true, to verify I have it up and running.
3. Next it is important to understand what unit testing is testing. It is testing single units of your code in isolation. For example, testing a method for verifying the number of items in a shopping basket, should only focus on testing the number of items in the basket, and not be worrying about asserting that the add or update items methods work. (You should have separate tests that verify this, and you should be confident enough in those tests that you should be able to say.. "This works and I can use it in another test". The focus needs to be on the counting method).
4. I would now look to touch on the application ever so slightly. Basically, we want to write an __autoload method, so we don't have to include all our classes for testing everytime. Now the sneaky bit here, is that we are going to override the database connection later, and depending on how this interaction is working, if it's inline then this will be messy, but if it is from a class that handles your database connections and queries (eg some sort of PDO or mysql_ class), this might still get messy.
We want to have an autoload function that will search our application and include the class automatically, so we hope that some form of naming convension is used for helping this. If there are a few different places to search within the file system, ie if some classes may be in a /core/ folder, or if some may be within an individual module, you'll probably want to loop through these folders and check for the class file to include when it is first instanciated within the code. The plan is to add a new location to this list, a place to check before anywhere else, a dummy or testing folder. This will allow us to make a test database wrapper and by calling this the same thing as the live one, it will be included before the autoload method finds the live class, and the tests will use that.
5. If I have lots of files which do'nt rely on the database class, or any class, I will try to leave those for a bit, and find one that does to get me started. I move away from the MySql database, and move towards a file based structure, or in one simple instance I used an in memory php array. I found understanding this was important as when I began instanciating my classes for testing, there was some information being returned from the database or checks being made that certain information was set/not set, and having at least a simple layer of abstration in there to begin with help me get my head around how much the database was being queried.
6. Now its a case of slowly building up the unit tests. For each class, I worked through each method that was there, attempting to do a bit of set up, eg, create a shopping cart. I would then do my assert, eg, assert that cart has no items. Then destroy the cart. The clean up is important to avoid any interference between tests. You want to ensure that each test has a clean start, you should know the state of the system before any test, no matter which tests have ran before. Eg, testing an add item to cart method, should empty and destroy the cart before moving to the next test that counting the number of items present, and you should be aware that it is, as these could interfere if not.
7. While working away through legacy code, I would focus on getting some base of tests for existing classes and models, with a view of going through any code which doesn't utilise classes later. I definitely think having all your model code in classes is the best way towards testing, your controllers and script pages should be as simple as possible to read and follow.
8. Now you have a suite of tests for your code, and can think more about refactoring and rewriting it now. You can make changes to your system in the knowledge that you can run these tests at any time, and know within 10 or 20 seconds if you have broken any of the existing functionality.
Disclaimer: I am not saying this is a fool proof or perfect way to get into testing, but it is the way I have got into it myself. I am also by no means an expert on this subject, and would class myself very novice in the area of testing. However I would get some information out there for anyone new to, or thinking about looking at unit testing.
Comments welcome.
Monday, 17 December 2012
More Than a Dot
No one knows if it was a man, or a woman, or a child that first did it, but we do know that about 40,000 years ago, someone put a faint red dot on the wall of a cave in Spain.
Humankind has always felt a burning desire to record information and transmit it into the future. What that dot meant, no one really knows. Perhaps it was the first expression of binary?
Perhaps it was just a dot.
7,000 years later, and our ancestors were expressing themselves in the form of horses, panthers, cave bears, mammoths, and much more inside different caves, this time in the south of France. It’s the first example of recorded history, the first transmission into the future.
It took many thousands more years until we discovered writing. The Sumerians started scraping sigils into clay tablets and baking them — an act which preserved them almost forever, and because of it we, know so much about their society and the way it worked.
Now, of course, we have the Web, and the world has been transformed. So much information is available that our biggest problems are sorting, classifying, filtering, and absorbing, and the rate at which information is growing is staggering. In 2010, we broke the zettabyte barrier, and in 2011, nearly 1.8 zettabytes of information was created, stored, and replicated. Numbers aren’t available for 2012 yet, but we can safely assume that it will have been greater, and that 2013 will be greater still.
The scale is phenomenal. If a penny represented every gigabyte of storage that was consumed, you could use nothing but pennies to construct a 1:1 replica of the Empire State Building in New York and still have some change left over for a cup of coffee when you were done with your labors.
As we are creating all this information, the way in which we are realizing we can use it is changing. We’re structuring the unstructured, and turning the unknowable into knowledge. We’re making tools for navigating the data more effectively, and we’re learning that sometimes raw information is knowledge — you just have to know to ask the right questions.
Improvements in the browser technology available is allowing web developers to push the boundaries of what was thought possible. Our interfaces are richer, and the interactivity is far greater than it ever has been before.
It’s not just the browser — far from it. As our processing spills out of our homes and offices and onto our smart phones and smart devices, the Web has ceased to be just about the browser. The Web has become a message bus. A conduit by which information is ferried back and forth, not just from server to client, but from client to client as well. I can share my thoughts and feelings with friends on the other side of the world instantly, and to the extent that sometimes it doesn't feel as though there is any distance at all.
Each development spurs a rush of new apps and new ways of looking at older data, which creates new data that pushes us forward again. It’s not that our time to market is shrinking, it’s that our time to market has become instantaneous, and our tools and techniques are adapting to cope with that.
However, as the end of the year approaches, I get reflective, and I find myself looking at the Web and worrying about one thing. 40,000 years from now, when our descendants cast their gaze back on the Web, will they understand what we were saying?
Have we preserved the knowledge we’re creating for future generations, or are we just doing the best we can to keep up with the rate that we’re creating it? Can we be sure that we’re doing the equivalent of writing on clay tablets, baked in the sun and made to last forever?
And, once we are sure of that, will our children’s children’s children still be able to make sense of what we have to say, or have we become so myopic that we no longer care about transmissions further into the future than our own lifetimes? Will our apps still serve them as they have served us? Are we the architects of a library, or of Babylon?
I’d certainly like to think we’re creating more than just a dot.
Wednesday, 24 October 2012
Introduction to TDD
1. What is TDD?
| The steps of test first development (TFD) are overviewed in the UML activity diagram of Figure 1. The first step is to quickly add a test, basically just enough code to fail. Next you run your tests, often the complete test suite although for sake of speed you may decide to run only a subset, to ensure that the new test does in fact fail. You then update your functional code to make it pass the new tests. The fourth step is to run your tests again. If they fail you need to update your functional code and retest. Once the tests pass the next step is to start over (you may first need to refactor any duplication out of your design as needed, turning TFD into TDD). |
There are two levels of TDD:
Figure 2 depicts a UML activity diagram showing how ATDD and developer TDD fit together. Ideally, you'll write a single acceptance test, then to implement the production code required to fulfill that test you'll take a developer TDD approach. This in turn requires you to iterate several times through the write a test, write production code, get it working cycle at the developer TDD level.
|
Kent Beck, who popularized TDD in eXtreme Programming (XP) (Beck 2000), defines two simple rules for TDD (Beck 2003). First, you should write new business code only when an automated test has failed. Second, you should eliminate any duplication that you find. Beck explains how these two simple rules generate complex individual and group behavior:
|
- Run fast (they have short setups, run times, and break downs).
- Run in isolation (you should be able to reorder them).
- Use data that makes them easy to read and to understand.
- Use real data (e.g. copies of production data) when they need to.
- Represent one step towards your overall goal.
2. TDD and Traditional Testing
TDD is primarily a specification technique with a side effect of ensuring that your source code is thoroughly tested at a confirmatory level. However, there is more to testing than this. Particularly at scale you'll still need to consider other agile testing techniques such as pre-production integration testing and investigative testing. Much of this testing can also be done early in your project if you choose to do so (and you should).
With traditional testing a successful test finds one or more defects. It is the same with TDD; when a test fails you have made progress because you now know that you need to resolve the problem. More importantly, you have a clear measure of success when the test no longer fails. TDD increases your confidence that your system actually meets the requirements defined for it, that your system actually works and therefore you can proceed with confidence.
|
|
3. TDD and Documentation
4. Test-Driven Database Development
5. Scaling TDD via Agile Model-Driven Development (AMDD)
- TDD shortens the programming feedback loop whereas AMDD shortens the modeling feedback loop.
- TDD provides detailed specification (tests) whereas AMDD is better for thinking through bigger issues.
- TDD promotes the development of high-quality code whereas AMDD promotes high-quality communication with your stakeholders and other developers.
- TDD provides concrete evidence that your software works whereas AMDD supports your team, including stakeholders, in working toward a common understanding.
- TDD “speaks” to programmers whereas AMDD speaks to business analysts, stakeholders, and data professionals.
- TDD is provides very finely grained concrete feedback on the order of minutes whereas AMDD enables verbal feedback on the order minutes (concrete feedback requires developers to follow the practice Prove It With Code and thus becomes dependent on non-AM techniques).
- TDD helps to ensure that your design is clean by focusing on creation of operations that are callable and testable whereas AMDD provides an opportunity to think through larger design/architectural issues before you code.
- TDD is non-visually oriented whereas AMDD is visually oriented.
- Both techniques are new to traditional developers and therefore may be threatening to them.
- Both techniques support evolutionary development.
6. Why TDD?
7. Myths and Misconceptions
| Myth | Reality |
| You create a 100% regression test suite | Although this sounds like a good goal, and it is, it unfortunately isn't realistic for several reasons:
|
| The unit tests form 100% of your design specification | People new to agile software development, or people claiming to be agile but who really aren't, or perhaps people who have never been involved with an actual agile project, will sometimes say this. The reality is that the unit test form a fair bit of the design specification, similarly acceptance tests form a fair bit of your requirements specification, but there's more to it than this. As Figure 4 indicates, agilists do in factmodel (and document for that matter), it's just that we're very smart about how we do it. Because you think about the production code before you write it, you effectively perform detailed design as I highly suggest reading my Single Source Information: An Agile Practice for Effective Documentation article. |
| You only need to unit test | For all but the simplest systems this is completely false. The agile community is very clear about the need for a host of other testing techniques. |
| TDD is sufficient for testing | TDD, at the unit/developer test as well as at the customer test level, is only part of your overall testing efforts. At best it comprises your confirmatory testing efforts, but as Figure 5 shows you must also be concerned about independent testing efforts which go beyond this. See Agile Testing and Quality Strategies: Reality over Rhetoric for details about agile testing strategies. |
| TDD doesn't scale | This is partly true, although easy to overcome. TDD scalability issues include:
|
8. Who is Actually Doing This?
9. Summary
10. Tools
- cpputest
- csUnit (.Net)
- CUnit
- DUnit (Delphi)
- DBFit
- DBUnit
- DocTest (Python)
- Googletest
- JMock
- JUnit
- Moq
- NDbUnit
- NUnit
- OUnit
- PHPUnit
- PyUnit (Python)
- SimpleTest
- TestNG
- TestOoB (Python)
- Test::Unit (Ruby)
- VBUnit
- XTUnit
- xUnit.net
11. References and Suggested Online Readings
http://www.agiledata.org/essays/tdd.html
The road to CAIO
The Engineer Who Stops Owning Only the Code For much of a software engineer’s career, progression appears straightforward. Junior Develope...
-
The Concept of True North in Lean Methodology In the world of Lean methodology, one of the most fundamental and guiding principles is ...
-
As engineering teams grow, so does the need for leadership that isn’t purely managerial. Enter the Staff Engineer —a senior individual contr...
-
Engineering managers are often expected to provide clarity amid chaos. You may have a team delivering features at full speed, but without a...